nano-run/server/auth.go

130 lines
3.0 KiB
Go

package server
import (
"errors"
"net/http"
"github.com/dgrijalva/jwt-go"
"golang.org/x/crypto/bcrypt"
)
func (cfg Unit) enableAuthorization() func(handler http.Handler) http.Handler {
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())
}
if len(handlers) == 0 {
return func(handler http.Handler) http.Handler {
return handler
}
}
return func(handler http.Handler) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
var authorized bool
for _, h := range handlers {
if h(request) {
authorized = true
break
}
}
if !authorized {
writer.WriteHeader(http.StatusForbidden)
return
}
handler.ServeHTTP(writer, request)
})
}
}
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) Create() AuthHandlerFunc {
header := cfg.Header
if header == "" {
header = "Authorization"
}
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) Create() AuthHandlerFunc {
param := cfg.Param
if param == "" {
param = "token"
}
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) Create() AuthHandlerFunc {
header := cfg.Header
if header == "" {
header = "X-Api-Token"
}
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
}
}