1 Star 0 Fork 0

newlife/gnatsd

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
auth.go 5.51 KB
一键复制 编辑 原始数据 按行查看 历史
// Copyright 2012-2017 Apcera Inc. All rights reserved.
package server
import (
"crypto/tls"
"fmt"
"strings"
"golang.org/x/crypto/bcrypt"
)
// Authentication is an interface for implementing authentication
type Authentication interface {
// Check if a client is authorized to connect
Check(c ClientAuthentication) bool
}
// ClientAuthentication is an interface for client authentication
type ClientAuthentication interface {
// Get options associated with a client
GetOpts() *clientOpts
// If TLS is enabled, TLS ConnectionState, nil otherwise
GetTLSConnectionState() *tls.ConnectionState
// Optionally map a user after auth.
RegisterUser(*User)
}
// User is for multiple accounts/users.
type User struct {
Username string `json:"user"`
Password string `json:"password"`
Permissions *Permissions `json:"permissions"`
}
// clone performs a deep copy of the User struct, returning a new clone with
// all values copied.
func (u *User) clone() *User {
if u == nil {
return nil
}
clone := &User{}
*clone = *u
clone.Permissions = u.Permissions.clone()
return clone
}
// Permissions are the allowed subjects on a per
// publish or subscribe basis.
type Permissions struct {
Publish []string `json:"publish"`
Subscribe []string `json:"subscribe"`
}
// clone performs a deep copy of the Permissions struct, returning a new clone
// with all values copied.
func (p *Permissions) clone() *Permissions {
if p == nil {
return nil
}
clone := &Permissions{}
if p.Publish != nil {
clone.Publish = make([]string, len(p.Publish))
copy(clone.Publish, p.Publish)
}
if p.Subscribe != nil {
clone.Subscribe = make([]string, len(p.Subscribe))
copy(clone.Subscribe, p.Subscribe)
}
return clone
}
// configureAuthorization will do any setup needed for authorization.
// Lock is assumed held.
func (s *Server) configureAuthorization() {
if s.opts == nil {
return
}
// Snapshot server options.
opts := s.getOpts()
// Check for multiple users first
// This just checks and sets up the user map if we have multiple users.
if opts.CustomClientAuthentication != nil {
s.info.AuthRequired = true
} else if opts.Users != nil {
s.users = make(map[string]*User)
for _, u := range opts.Users {
s.users[u.Username] = u
}
s.info.AuthRequired = true
} else if opts.Username != "" || opts.Authorization != "" {
s.info.AuthRequired = true
} else {
s.users = nil
s.info.AuthRequired = false
}
}
// checkAuthorization will check authorization based on client type and
// return boolean indicating if client is authorized.
func (s *Server) checkAuthorization(c *client) bool {
switch c.typ {
case CLIENT:
return s.isClientAuthorized(c)
case ROUTER:
return s.isRouterAuthorized(c)
default:
return false
}
}
// isClientAuthorized will check the client against the proper authorization method and data.
// This could be token or username/password based.
func (s *Server) isClientAuthorized(c *client) bool {
// Snapshot server options.
opts := s.getOpts()
// Check custom auth first, then multiple users, then token, then single user/pass.
if s.opts.CustomClientAuthentication != nil {
return s.opts.CustomClientAuthentication.Check(c)
} else if s.users != nil {
user, ok := s.users[c.opts.Username]
if !ok {
return false
}
ok = comparePasswords(user.Password, c.opts.Password)
// If we are authorized, register the user which will properly setup any permissions
// for pub/sub authorizations.
if ok {
c.RegisterUser(user)
}
return ok
} else if opts.Authorization != "" {
return comparePasswords(opts.Authorization, c.opts.Authorization)
} else if opts.Username != "" {
if opts.Username != c.opts.Username {
return false
}
return comparePasswords(opts.Password, c.opts.Password)
}
return true
}
// checkRouterAuth checks optional router authorization which can be nil or username/password.
func (s *Server) isRouterAuthorized(c *client) bool {
// Snapshot server options.
opts := s.getOpts()
if s.opts.CustomRouterAuthentication != nil {
return s.opts.CustomRouterAuthentication.Check(c)
}
if opts.Cluster.Username == "" {
return true
}
if opts.Cluster.Username != c.opts.Username {
return false
}
return comparePasswords(opts.Cluster.Password, c.opts.Password)
}
// removeUnauthorizedSubs removes any subscriptions the client has that are no
// longer authorized, e.g. due to a config reload.
func (s *Server) removeUnauthorizedSubs(c *client) {
c.mu.Lock()
if c.perms == nil {
c.mu.Unlock()
return
}
subs := make(map[string]*subscription, len(c.subs))
for sid, sub := range c.subs {
subs[sid] = sub
}
c.mu.Unlock()
for sid, sub := range subs {
if !c.canSubscribe(sub.subject) {
_ = s.sl.Remove(sub)
c.mu.Lock()
delete(c.subs, sid)
c.mu.Unlock()
c.sendErr(fmt.Sprintf("Permissions Violation for Subscription to %q (sid %s)",
sub.subject, sub.sid))
s.Noticef("Removed sub %q for user %q - not authorized",
string(sub.subject), c.opts.Username)
}
}
}
// Support for bcrypt stored passwords and tokens.
const bcryptPrefix = "$2a$"
// isBcrypt checks whether the given password or token is bcrypted.
func isBcrypt(password string) bool {
return strings.HasPrefix(password, bcryptPrefix)
}
func comparePasswords(serverPassword, clientPassword string) bool {
// Check to see if the server password is a bcrypt hash
if isBcrypt(serverPassword) {
if err := bcrypt.CompareHashAndPassword([]byte(serverPassword), []byte(clientPassword)); err != nil {
return false
}
} else if serverPassword != clientPassword {
return false
}
return true
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/newlife/gnatsd.git
git@gitee.com:newlife/gnatsd.git
newlife
gnatsd
gnatsd
v1.0.6

搜索帮助