167 lines
3.9 KiB
Go
167 lines
3.9 KiB
Go
package httphandlers
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/fiskerinc/cloud-services/pkg/adminroles"
|
|
"github.com/fiskerinc/cloud-services/pkg/common/authproviders"
|
|
c "github.com/fiskerinc/cloud-services/pkg/common/context"
|
|
"github.com/fiskerinc/cloud-services/pkg/db/queries"
|
|
"github.com/fiskerinc/cloud-services/pkg/jwt"
|
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
|
"github.com/fiskerinc/cloud-services/pkg/utils"
|
|
)
|
|
|
|
var errNoUsername = errors.New("no username")
|
|
|
|
type AuthCheckerInterface interface {
|
|
GetHandler(requiredRoles map[string][]adminroles.RoleID, next http.HandlerFunc) http.HandlerFunc
|
|
Check(requiredRoles map[string][]adminroles.RoleID, r *http.Request) (context.Context, error)
|
|
GetValidator() jwt.JWTValidatorInterface
|
|
SetGroupKey(string)
|
|
Close()
|
|
}
|
|
|
|
type AuthJWTToken struct {
|
|
groupkey string
|
|
apiCalls queries.APICallsInterface
|
|
validator jwt.JWTValidatorInterface
|
|
AuthBase
|
|
}
|
|
|
|
func (a *AuthJWTToken) GetValidator() jwt.JWTValidatorInterface {
|
|
if a.validator == nil {
|
|
a.validator = jwt.NewJWTValidator("")
|
|
}
|
|
return a.validator
|
|
}
|
|
|
|
func (a *AuthJWTToken) GroupKey() string {
|
|
if len(a.groupkey) == 0 {
|
|
return "custom:groups"
|
|
}
|
|
|
|
return a.groupkey
|
|
}
|
|
|
|
func (a *AuthJWTToken) SetGroupKey(gp string) {
|
|
a.groupkey = gp
|
|
}
|
|
|
|
func (a *AuthJWTToken) GetHandler(requiredRoles map[string][]adminroles.RoleID, next http.HandlerFunc) http.HandlerFunc {
|
|
wrapper := func(w http.ResponseWriter, r *http.Request) {
|
|
ctx, err := a.Check(requiredRoles, r)
|
|
if err != nil {
|
|
logger.Warn().Msgf("AuthJWTToken %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 *AuthJWTToken) 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, err := jwt.GetAuthorizationHeader(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
payload, err := a.GetValidator().ValidateToken(token.Token)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if username, ok := a.getUsername(payload); ok && username != "" {
|
|
ctx := context.WithValue(r.Context(), ClientIDContextKey, username)
|
|
*r = *r.WithContext(ctx)
|
|
} else {
|
|
return nil, errNoUsername
|
|
}
|
|
|
|
provider := a.getProvider(payload)
|
|
roles, ok := a.getRolesForProvider(provider, requiredRoles)
|
|
if !ok {
|
|
return nil, errors.New(adminroles.MissingPermissionError)
|
|
}
|
|
|
|
checker := adminroles.RolesChecker{}
|
|
checker.SetRequiredRoles(roles)
|
|
err = checker.CheckGroups(payload[a.GroupKey()])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ctx := a.addContext(r.Context(), c.ProviderKey, provider)
|
|
|
|
return ctx, nil
|
|
}
|
|
|
|
func (a *AuthJWTToken) getProvider(payload map[string]interface{}) string {
|
|
var ok bool
|
|
var data interface{}
|
|
var identities []interface{}
|
|
var identity map[string]interface{}
|
|
var provider string
|
|
|
|
if data, ok = payload["identities"]; !ok {
|
|
return authproviders.Default
|
|
}
|
|
|
|
if identities, ok = data.([]interface{}); !ok || len(identities) != 1 {
|
|
return authproviders.Default
|
|
}
|
|
|
|
if identity, ok = identities[0].(map[string]interface{}); !ok {
|
|
return authproviders.Default
|
|
}
|
|
|
|
if provider, ok = identity["providerName"].(string); !ok {
|
|
return authproviders.Default
|
|
}
|
|
|
|
return provider
|
|
|
|
}
|
|
|
|
func (a *AuthJWTToken) Close() {
|
|
// nothing to dispose here
|
|
}
|
|
|
|
func (a *AuthJWTToken) getUsername(payload map[string]interface{}) (string, bool) {
|
|
username, ok := payload["cognito:username"].(string)
|
|
if ok && username != "" {
|
|
return username, true
|
|
}
|
|
|
|
username, ok = payload["username"].(string)
|
|
if ok && username != "" {
|
|
return username, true
|
|
}
|
|
|
|
username, ok = payload["preferred_username"].(string)
|
|
if ok && username != "" {
|
|
return username, true
|
|
}
|
|
|
|
username, ok = payload["email"].(string)
|
|
if ok && username != "" {
|
|
return username, true
|
|
}
|
|
|
|
return "", false
|
|
}
|