From 7c94ec920c3b378993ed18135c2a07086caffd0a Mon Sep 17 00:00:00 2001 From: Pavel Antonov <antonov@perx.ru> Date: Thu, 3 Oct 2024 15:47:53 +0400 Subject: [PATCH] WIP --- auth.go | 153 ++++++++++++++++++++++++++++++++++++++++++++++ pkg/auth/auth.go | 1 + pkg/roles/role.go | 6 +- 3 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 auth.go create mode 100644 pkg/auth/auth.go diff --git a/auth.go b/auth.go new file mode 100644 index 00000000..871835f6 --- /dev/null +++ b/auth.go @@ -0,0 +1,153 @@ +package perxis + +import ( + "context" + "runtime" + "strings" + + "git.perx.ru/perxis/perxis-go/pkg/errors" +) + +var ( + ErrAccessDenied = errors.New("access denied") +) + +type Principal interface { + GetID() string +} + +// Authenticator интерфейс для аутентификации +type Authenticator interface { + // Authenticate аутентификация + Authenticate(ctx context.Context) (Principal, error) +} + +type Authorization struct { + Authorizer Authorizer +} + +// Authorizer интерфейс для авторизации +type Authorizer interface { + Authorize(principal Principal, action string, resource any) (*Authorization, error) +} + +var ( + authorizer Authorizer + authenticator Authenticator +) + +func SetAuthorizer(a Authorizer) { + authorizer = a +} + +func SetAuthenticator(a Authenticator) { + authenticator = a +} + +func Authenticate(ctx context.Context) (Principal, error) { + if authenticator == nil { + return nil, nil + } + return authenticator.Authenticate(ctx) +} + +func Authorize(principal Principal, action string, resource any) (*Authorization, error) { + if authorizer == nil { + return nil, nil + } + return authorizer.Authorize(principal, action, resource) +} + +func AuthorizeContext(ctx context.Context, action string, resource any) (*Authorization, error) { + principal, err := Authenticate(ctx) + if err != nil { + return nil, err + } + return Authorize(principal, action, resource) +} + +func IsAllowed(ctx context.Context, res any) (*Authorization, error) { + pc, _, _, _ := runtime.Caller(1) + parts := strings.Split(runtime.FuncForPC(pc).Name(), ".") + action := parts[len(parts)-1] + return AuthorizeContext(ctx, action, res) +} + +//type AccessRequest struct { +// Resource any // Ресурс для которого запрашивается доступ +// Subject any // Субъект, который запрашивает доступ +// Action string // Действие, которое запрашивается +//} +// +//type AccessResponse struct { +// IsAllowed bool +// Filter func(action string, v any) any +// Err error +//} +// +//type Policy interface { +// IsAllowed(ctx context.Context, req *AccessRequest) (*AccessResponse, error) +//} +// +//func IsMethodAllowed(ctx context.Context, resource any) (*AccessResponse, error) { +// pc, _, _, _ := runtime.Caller(1) +// parts := strings.Split(runtime.FuncForPC(pc).Name(), ".") +// req := &AccessRequest{Action: parts[len(parts)-1], Resource: resource} +// return IsAllowed(ctx, req) +//} +// +//func IsAllowed(ctx context.Context, req *AccessRequest) (*AccessResponse, error) { +// if req != nil && req.Subject == nil { +// principal := GetPrincipal(ctx) +// if principal != nil { +// req.Subject = principal +// } +// } +// +// fmt.Printf("IsAllowed: %v\n", req) +// return DefaultPolicy.IsAllowed(ctx, req) +//} +// +//type Policies []Policy +// +//func (p Policies) IsAllowed(ctx context.Context, req *AccessRequest) (*AccessResponse, error) { +// for _, policy := range p { +// if resp, err := policy.IsAllowed(ctx, req); resp != nil || err != nil { +// return resp, err +// } +// } +// return nil, nil +//} +// +//type defaultPolicy struct { +// policies []Policy +//} +// +//func (d *defaultPolicy) IsAllowed(ctx context.Context, req *AccessRequest) (*AccessResponse, error) { +// if p, ok := req.Subject.(Policy); ok { +// if resp, err := p.IsAllowed(ctx, req); resp != nil || err != nil { +// return resp, err +// } +// } +// +// for _, policy := range d.policies { +// if resp, err := policy.IsAllowed(ctx, req); resp != nil || err != nil { +// return resp, err +// } +// } +// +// // Policy not found +// return nil, ErrAccessDenied +//} +// +//type AllowPolicy struct{} +// +//func (AllowPolicy) IsAllowed(ctx context.Context, req *AccessRequest) (*AccessResponse, error) { +// return &AccessResponse{IsAllowed: true, Filter: func(action string, v any) any { return v }}, nil +//} +// +//type DenyPolicy struct{} +// +//func (DenyPolicy) IsAllowed(ctx context.Context, req *AccessRequest) (*AccessResponse, error) { +// return nil, ErrAccessDenied +//} diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go new file mode 100644 index 00000000..8832b06d --- /dev/null +++ b/pkg/auth/auth.go @@ -0,0 +1 @@ +package auth diff --git a/pkg/roles/role.go b/pkg/roles/role.go index 76520f8f..1557de77 100644 --- a/pkg/roles/role.go +++ b/pkg/roles/role.go @@ -52,7 +52,7 @@ func (r Role) Clone() *Role { } func (r Role) CanAccessEnvironment(ctx context.Context, env *environments.Environment, service environments.Environments) bool { - if env.SpaceID == "" || env.ID == "" { + if env.ID == "" { return false } @@ -66,13 +66,13 @@ func (r Role) CanAccessEnvironment(ctx context.Context, env *environments.Enviro } // Если окружение передано не полное, это означает, что надо его перезапросить - if env.Description == "" && env.Aliases == nil && env.StateInfo == nil { + if service != nil && env.Description == "" && env.Aliases == nil && env.StateInfo == nil { if data.GlobMatch(env.ID, r.Environments...) { return true } var err error - env, err = service.Get(ctx, env.SpaceID, env.ID) + env, err = service.Get(ctx, r.SpaceID, env.ID) if err != nil || env == nil { return false } -- GitLab