nano-run/server/auth.go

146 lines
3.1 KiB
Go

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
}