package server import ( "errors" "net/http" "sort" "github.com/dgrijalva/jwt-go" "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" ) func (cfg Unit) enableAuthorization() func(gctx *gin.Context) { var handlers []AuthHandlerFunc if cfg.Authorization.JWT.Enable { handlers = append(handlers, cfg.Authorization.JWT.Create()) } if cfg.Authorization.QueryToken.Enable { handlers = append(handlers, cfg.Authorization.QueryToken.Create()) } if cfg.Authorization.HeaderToken.Enable { handlers = append(handlers, cfg.Authorization.HeaderToken.Create()) } if cfg.Authorization.Basic.Enable { handlers = append(handlers, cfg.Authorization.Basic.Create()) } return func(gctx *gin.Context) { var authorized = len(handlers) == 0 for _, h := range handlers { if h(gctx.Request) { authorized = true break } } if !authorized { gctx.AbortWithStatus(http.StatusForbidden) return } gctx.Next() } } type AuthHandlerFunc func(req *http.Request) bool type JWT struct { Header string `yaml:"header"` // JWT header - by default Authorization Secret string `yaml:"secret"` // key to verify JWT } func (cfg JWT) GetHeader() string { if cfg.Header == "" { return "Authorization" } return cfg.Header } func (cfg JWT) Create() AuthHandlerFunc { header := cfg.GetHeader() return func(req *http.Request) bool { rawToken := req.Header.Get(header) t, err := jwt.Parse(rawToken, func(token *jwt.Token) (interface{}, error) { if token.Method != jwt.SigningMethodHS256 { return nil, errors.New("unknown method") } return []byte(cfg.Secret), nil }) return err == nil && t.Valid } } type QueryToken struct { Param string `yaml:"param"` // query name - by default 'token' Tokens []string `yaml:"tokens"` // allowed tokens } func (cfg QueryToken) GetParam() string { if cfg.Param == "" { return "token" } return cfg.Param } func (cfg QueryToken) Create() AuthHandlerFunc { param := cfg.GetParam() tokens := map[string]bool{} for _, k := range cfg.Tokens { tokens[k] = true } return func(req *http.Request) bool { token := req.URL.Query().Get(param) return tokens[token] } } type HeaderToken struct { Header string `yaml:"header"` // header name - by default X-Api-Token Tokens []string `yaml:"tokens"` // allowed tokens } func (cfg HeaderToken) GetHeader() string { if cfg.Header == "" { return "X-Api-Token" } return cfg.Header } func (cfg HeaderToken) Create() AuthHandlerFunc { header := cfg.GetHeader() tokens := map[string]bool{} for _, k := range cfg.Tokens { tokens[k] = true } return func(req *http.Request) bool { token := req.URL.Query().Get(header) return tokens[token] } } type Basic struct { Users map[string]string `yaml:"users"` // users -> bcrypted password map } func (cfg Basic) Create() AuthHandlerFunc { return func(req *http.Request) bool { u, p, ok := req.BasicAuth() if !ok { return false } h, ok := cfg.Users[u] if !ok { return false } return bcrypt.CompareHashAndPassword([]byte(h), []byte(p)) == nil } } func (cfg Basic) Logins() []string { var ans []string for name := range cfg.Users { ans = append(ans, name) } sort.Strings(ans) return ans }