diff --git a/auth/auth.go b/auth/auth.go index 4869f66..44a5e4a 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -178,10 +178,16 @@ func (g Group) HasPermission(permission Permission) bool { // A User implements SecurityPrincipal and represents an authenticated person type User struct { - Id string - Name string + // the user internal id + Id string + // the name of user + Name string + // the id/name of the platform were the user was authenticated (for example Google, Linkedin, Internal, etc) IdentityPlatform string - Groups []Group + // the security groups where this user belongs + Groups []Group + // a field where any additional data to this user can be attached + Attachment any } func (u User) Identity() string { diff --git a/middleware/authz.go b/middleware/authz.go index da4f482..3fa53f5 100644 --- a/middleware/authz.go +++ b/middleware/authz.go @@ -10,42 +10,56 @@ import ( "github.com/ixtendio/gofre/response" ) +type PermissionsSupplierFunc func(ctx context.Context, mc path.MatchingContext) ([]auth.Permission, error) + // AuthorizeAll checks if the authenticated auth.SecurityPrincipal has all the requested permissions // An errors.ErrUnauthorizedRequest error is returned if not all permissions are allowed to be executed // by the current auth.SecurityPrincipal func AuthorizeAll(permissions ...auth.Permission) Middleware { - return func(handler handler.Handler) handler.Handler { - return func(ctx context.Context, mc path.MatchingContext) (resp response.HttpResponse, err error) { - securityPrincipal := auth.GetSecurityPrincipalFromContext(ctx) - if securityPrincipal == nil { - return nil, errors.ErrUnauthorizedRequest - } - for _, permission := range permissions { - if !securityPrincipal.HasPermission(permission) { - return nil, errors.ErrUnauthorizedRequest - } - } - return handler(ctx, mc) - } - } + return Authorize(func(ctx context.Context, mc path.MatchingContext) ([]auth.Permission, error) { + return permissions, nil + }, true) } // AuthorizeAny checks if the authenticated auth.SecurityPrincipal has at least one from the requested permissions // An errors.ErrUnauthorizedRequest error is returned if not at least one permission is allowed to be executed // by the current auth.SecurityPrincipal func AuthorizeAny(permissions ...auth.Permission) Middleware { + return Authorize(func(ctx context.Context, mc path.MatchingContext) ([]auth.Permission, error) { + return permissions, nil + }, false) +} + +// Authorize checks if the authenticated auth.SecurityPrincipal has all permission if the parameter matchAllPermissions is true +// or at least one permission, otherwise +// An errors.ErrUnauthorizedRequest error is returned if not at least one permission is allowed to be executed +// by the current auth.SecurityPrincipal +func Authorize(supplier PermissionsSupplierFunc, matchAllPermissions bool) Middleware { return func(handler handler.Handler) handler.Handler { return func(ctx context.Context, mc path.MatchingContext) (resp response.HttpResponse, err error) { + permissions, err := supplier(ctx, mc) + if err != nil { + return nil, err + } securityPrincipal := auth.GetSecurityPrincipalFromContext(ctx) if securityPrincipal == nil { return nil, errors.ErrUnauthorizedRequest } - for _, permission := range permissions { - if securityPrincipal.HasPermission(permission) { - return handler(ctx, mc) + if matchAllPermissions { + for _, permission := range permissions { + if !securityPrincipal.HasPermission(permission) { + return nil, errors.ErrUnauthorizedRequest + } } + return handler(ctx, mc) + } else { + for _, permission := range permissions { + if securityPrincipal.HasPermission(permission) { + return handler(ctx, mc) + } + } + return nil, errors.ErrUnauthorizedRequest } - return nil, errors.ErrUnauthorizedRequest } } } diff --git a/middleware/errors.go b/middleware/errors.go index 090ef6d..f5c0cd0 100644 --- a/middleware/errors.go +++ b/middleware/errors.go @@ -12,7 +12,22 @@ import ( type ResponseSupplier func(statusCode int, err error) response.HttpResponse -// ErrJsonResponse translate an error to a JSON error response +// Error2HttpStatusCode translates an error to an HTTP status code +var Error2HttpStatusCode = func(err error) int { + if _, ok := err.(errors.ErrBadRequest); ok { + return http.StatusBadRequest + } else if _, ok := err.(errors.ErrObjectNotFound); ok { + return http.StatusNotFound + } else if err == errors.ErrUnauthorizedRequest { + return http.StatusUnauthorized + } else if err == errors.ErrWrongCredentials || + err == errors.ErrAccessDenied { + return http.StatusForbidden + } + return http.StatusInternalServerError +} + +// ErrJsonResponse translates an error to a JSON response func ErrJsonResponse() Middleware { return ErrResponse(func(statusCode int, err error) response.HttpResponse { return response.JsonHttpResponse(statusCode, map[string]string{ @@ -21,23 +36,13 @@ func ErrJsonResponse() Middleware { }) } -// ErrResponse translate an error to an response.HttpResponse +// ErrResponse translates an error to an response.HttpResponse func ErrResponse(responseSupplier ResponseSupplier) Middleware { return func(handler handler.Handler) handler.Handler { return func(ctx context.Context, mc path.MatchingContext) (response.HttpResponse, error) { resp, err := handler(ctx, mc) if err != nil { - statusCode := http.StatusInternalServerError - if _, ok := err.(errors.ErrBadRequest); ok { - statusCode = http.StatusBadRequest - } else if _, ok := err.(errors.ErrObjectNotFound); ok { - statusCode = http.StatusNotFound - } else if err == errors.ErrUnauthorizedRequest { - statusCode = http.StatusUnauthorized - } else if err == errors.ErrWrongCredentials || - err == errors.ErrAccessDenied { - statusCode = http.StatusForbidden - } + statusCode := Error2HttpStatusCode(err) return responseSupplier(statusCode, err), nil } return resp, err