github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/util/auth/namespace/namespace.go (about) 1 package namespace 2 3 import ( 4 "context" 5 "errors" 6 7 "github.com/tickoalcantara12/micro/v3/service/auth" 8 merrors "github.com/tickoalcantara12/micro/v3/service/errors" 9 ) 10 11 var ( 12 // ErrUnauthorized is returned by Authorize when a context without a blank account tries to access 13 // a restricted namespace 14 ErrUnauthorized = errors.New("an account is required") 15 // ErrForbidden is returned by Authorize when a context is trying to access a namespace it doesn't 16 // have access to 17 ErrForbidden = errors.New("access denied to namespace") 18 ) 19 20 const ( 21 // DefaultNamespace used by the server 22 DefaultNamespace = "micro" 23 ) 24 25 // AuthorizeAdmin returns a service error if the context is not an admin that can access this namespace 26 // e.g. either an admin for the this namespace or an admin for micro 27 func AuthorizeAdmin(ctx context.Context, ns, method string) error { 28 if err := Authorize(ctx, ns, method); err != nil { 29 return err 30 } 31 32 adminAcc, ok := auth.AccountFromContext(ctx) 33 if !ok { 34 return merrors.Unauthorized(method, "Unauthorized") 35 } 36 37 // check it's an admin 38 if !hasTypeAndScope("user", "admin", adminAcc) && !hasTypeAndScope("service", "service", adminAcc) { 39 return merrors.Unauthorized(method, "Unauthorized") 40 } 41 return nil 42 } 43 44 // Authorize will return a service error if the context cannot access the given namespace 45 func Authorize(ctx context.Context, namespace, method string, opts ...AuthorizeOption) error { 46 if err := authorize(ctx, namespace); err == ErrForbidden { 47 return merrors.Forbidden(method, err.Error()) 48 } else if err == ErrUnauthorized { 49 return merrors.Unauthorized(method, err.Error()) 50 } else if err != nil { 51 return merrors.InternalServerError(method, err.Error()) 52 } 53 return nil 54 } 55 56 // authorize will return an error if the context cannot access the given namespace 57 func authorize(ctx context.Context, namespace string, opts ...AuthorizeOption) error { 58 // parse the options 59 var options AuthorizeOptions 60 for _, o := range opts { 61 o(&options) 62 } 63 64 // check to see if the namespace was made public 65 if namespace == options.PublicNamespace { 66 return nil 67 } 68 69 // accounts are always required so we can identify the caller. If auth is not configured, the noop 70 // auth implementation will return a blank account with the default namespace set, allowing the caller 71 // access to all resources 72 acc, ok := auth.AccountFromContext(ctx) 73 if !ok { 74 return ErrUnauthorized 75 } 76 77 // the server and admins can access all namespaces 78 if acc.Issuer == DefaultNamespace && (hasTypeAndScope("service", "service", acc) || hasTypeAndScope("user", "admin", acc)) { 79 return nil 80 } 81 82 // ensure the account is requesting access to it's own namespace 83 if acc.Issuer != namespace { 84 return ErrForbidden 85 } 86 // account should be of type user or service 87 if acc.Type != "user" && acc.Type != "service" { 88 return ErrForbidden 89 } 90 91 return nil 92 } 93 94 func hasTypeAndScope(atype, scope string, acc *auth.Account) bool { 95 if atype != acc.Type { 96 return false 97 } 98 for _, s := range acc.Scopes { 99 if s == scope { 100 return true 101 } 102 } 103 return false 104 } 105 106 // AuthorizeOptions are used to configure the Authorize method 107 type AuthorizeOptions struct { 108 PublicNamespace string 109 } 110 111 // AuthorizeOption sets an attribute on AuthorizeOptions 112 type AuthorizeOption func(o *AuthorizeOptions) 113 114 // Public indicates a namespace is public and can be accessed by anyone 115 func Public(ns string) AuthorizeOption { 116 return func(o *AuthorizeOptions) { 117 o.PublicNamespace = ns 118 } 119 }