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 }