152 lines
3.3 KiB
Go
152 lines
3.3 KiB
Go
package httphandlers
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
|
|
"fiskerinc.com/modules/adminroles"
|
|
"fiskerinc.com/modules/cache"
|
|
"fiskerinc.com/modules/common/authproviders"
|
|
c "fiskerinc.com/modules/common/context"
|
|
"fiskerinc.com/modules/db/queries"
|
|
"fiskerinc.com/modules/jwt"
|
|
"fiskerinc.com/modules/logger"
|
|
"fiskerinc.com/modules/utils"
|
|
)
|
|
|
|
var ErrorNoAPITokenHeader = errors.New("no api token header")
|
|
|
|
type AuthAPIToken struct {
|
|
APITokens queries.APITokensInterface
|
|
APICalls queries.APICallsInterface
|
|
JWTAuth bool
|
|
GroupKey string
|
|
authJWT *AuthJWTToken
|
|
cache *cache.APITokenCache
|
|
onceAuthJTW sync.Once
|
|
onceCache sync.Once
|
|
AuthBase
|
|
}
|
|
|
|
func (a *AuthAPIToken) GetHandler(requiredRoles map[string][]adminroles.RoleID, next http.HandlerFunc) http.HandlerFunc {
|
|
wrapper := func(w http.ResponseWriter, r *http.Request) {
|
|
var err error
|
|
var ctx context.Context
|
|
|
|
ctx, err = a.Check(requiredRoles, r)
|
|
if errors.Is(err, ErrorNoAPITokenHeader) && a.JWTAuth {
|
|
ctx, err = a.GetJWTAuth().Check(requiredRoles, r)
|
|
}
|
|
|
|
if err != nil {
|
|
logger.Warn().Msgf("AuthAPIToken %s %s '%v'", r.Method, r.RequestURI, err)
|
|
utils.RespError(w, http.StatusUnauthorized, err.Error())
|
|
return
|
|
}
|
|
|
|
if ctx != nil {
|
|
r = r.WithContext(ctx)
|
|
}
|
|
next.ServeHTTP(w, r)
|
|
}
|
|
|
|
return wrapper
|
|
}
|
|
|
|
func (a *AuthAPIToken) Check(requiredRoles map[string][]adminroles.RoleID, r *http.Request) (context.Context, error) {
|
|
// if there are no required roles, anyone can access
|
|
if !a.hasRoles(requiredRoles) {
|
|
return r.Context(), nil
|
|
}
|
|
|
|
token, ok := a.HasAPIToken(r)
|
|
if !ok {
|
|
return nil, ErrorNoAPITokenHeader
|
|
}
|
|
|
|
ctx := context.WithValue(r.Context(), ClientIDContextKey, token)
|
|
r = r.WithContext(ctx)
|
|
|
|
// API Token is hard coded as provider
|
|
r = utils.AUTHWriteProviderToRequest(authproviders.FiskerAPIKey, r)
|
|
roles, ok := a.getRolesForProvider(authproviders.FiskerAPIKey, requiredRoles)
|
|
if !ok {
|
|
return nil, errors.New(adminroles.MissingPermissionError)
|
|
}
|
|
|
|
err := a.checkAPIToken(roles, token)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ctx = a.addContext(r.Context(), c.ProviderKey, authproviders.FiskerAPIKey)
|
|
return ctx, nil
|
|
}
|
|
|
|
func (a *AuthAPIToken) HasAPIToken(r *http.Request) (string, bool) {
|
|
token, ok := r.Header[cache.ApiKeyHeader]
|
|
if !ok || len(token[0]) == 0 {
|
|
return "", false
|
|
}
|
|
|
|
return token[0], true
|
|
}
|
|
|
|
func (a *AuthAPIToken) checkAPIToken(requiredRoles []adminroles.RoleID, token string) error {
|
|
if len(requiredRoles) == 0 {
|
|
return nil
|
|
}
|
|
|
|
roles, err := a.APICache().Get(token)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
checker := adminroles.RolesChecker{}
|
|
checker.SetRequiredRoles(requiredRoles)
|
|
|
|
return checker.Check(strings.Split(roles, ","))
|
|
}
|
|
|
|
func (a *AuthAPIToken) GetJWTAuth() *AuthJWTToken {
|
|
a.onceAuthJTW.Do(func() {
|
|
if a.authJWT == nil {
|
|
a.authJWT = &AuthJWTToken{
|
|
groupkey: a.GroupKey,
|
|
apiCalls: a.APICalls,
|
|
}
|
|
}
|
|
})
|
|
|
|
return a.authJWT
|
|
}
|
|
|
|
func (a *AuthAPIToken) SetGroupKey(gp string) {
|
|
a.authJWT.SetGroupKey(gp)
|
|
}
|
|
|
|
func (a *AuthAPIToken) APICache() *cache.APITokenCache {
|
|
a.onceCache.Do(func() {
|
|
if a.cache == nil {
|
|
a.cache = &cache.APITokenCache{
|
|
APITokens: a.APITokens,
|
|
}
|
|
}
|
|
})
|
|
|
|
return a.cache
|
|
}
|
|
|
|
func (a *AuthAPIToken) GetValidator() jwt.JWTValidatorInterface {
|
|
return a.GetJWTAuth().GetValidator()
|
|
}
|
|
|
|
func (a *AuthAPIToken) Close() {
|
|
a.APITokens = nil
|
|
a.authJWT = nil
|
|
a.cache = nil
|
|
}
|