diff --git a/api/grpc/auth/v1/auth.pb.go b/api/grpc/auth/v1/auth.pb.go index 194f019814..47b356a3e0 100644 --- a/api/grpc/auth/v1/auth.pb.go +++ b/api/grpc/auth/v1/auth.pb.go @@ -70,11 +70,11 @@ func (x *AuthNReq) GetToken() string { type AuthNRes struct { state protoimpl.MessageState `protogen:"open.v1"` - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // token id - UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` // user id - UserRole uint32 `protobuf:"varint,3,opt,name=user_role,json=userRole,proto3" json:"user_role,omitempty"` // user role - Verified bool `protobuf:"varint,4,opt,name=verified,proto3" json:"verified,omitempty"` // verified user - TokenType uint32 `protobuf:"varint,5,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` // token type + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + UserRole uint32 `protobuf:"varint,3,opt,name=user_role,json=userRole,proto3" json:"user_role,omitempty"` + Verified bool `protobuf:"varint,4,opt,name=verified,proto3" json:"verified,omitempty"` + TokenType uint32 `protobuf:"varint,5,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -146,16 +146,15 @@ func (x *AuthNRes) GetTokenType() uint32 { type PolicyReq struct { state protoimpl.MessageState `protogen:"open.v1"` - TokenType uint32 `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` // Token type - Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"` // Domain - SubjectType string `protobuf:"bytes,3,opt,name=subject_type,json=subjectType,proto3" json:"subject_type,omitempty"` // Client or User - SubjectKind string `protobuf:"bytes,4,opt,name=subject_kind,json=subjectKind,proto3" json:"subject_kind,omitempty"` // ID or Token - SubjectRelation string `protobuf:"bytes,5,opt,name=subject_relation,json=subjectRelation,proto3" json:"subject_relation,omitempty"` // Subject relation - Subject string `protobuf:"bytes,6,opt,name=subject,proto3" json:"subject,omitempty"` // Subject value - Relation string `protobuf:"bytes,7,opt,name=relation,proto3" json:"relation,omitempty"` // Relation to filter - Permission string `protobuf:"bytes,8,opt,name=permission,proto3" json:"permission,omitempty"` // Action - Object string `protobuf:"bytes,9,opt,name=object,proto3" json:"object,omitempty"` // Object ID - ObjectType string `protobuf:"bytes,10,opt,name=object_type,json=objectType,proto3" json:"object_type,omitempty"` // Client, User, Group + Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` + SubjectType string `protobuf:"bytes,2,opt,name=subject_type,json=subjectType,proto3" json:"subject_type,omitempty"` + SubjectKind string `protobuf:"bytes,3,opt,name=subject_kind,json=subjectKind,proto3" json:"subject_kind,omitempty"` + SubjectRelation string `protobuf:"bytes,4,opt,name=subject_relation,json=subjectRelation,proto3" json:"subject_relation,omitempty"` + Subject string `protobuf:"bytes,5,opt,name=subject,proto3" json:"subject,omitempty"` + Relation string `protobuf:"bytes,6,opt,name=relation,proto3" json:"relation,omitempty"` + Permission string `protobuf:"bytes,7,opt,name=permission,proto3" json:"permission,omitempty"` + Object string `protobuf:"bytes,8,opt,name=object,proto3" json:"object,omitempty"` + ObjectType string `protobuf:"bytes,9,opt,name=object_type,json=objectType,proto3" json:"object_type,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -190,13 +189,6 @@ func (*PolicyReq) Descriptor() ([]byte, []int) { return file_auth_v1_auth_proto_rawDescGZIP(), []int{2} } -func (x *PolicyReq) GetTokenType() uint32 { - if x != nil { - return x.TokenType - } - return 0 -} - func (x *PolicyReq) GetDomain() string { if x != nil { return x.Domain @@ -261,15 +253,15 @@ func (x *PolicyReq) GetObjectType() string { } type PATReq struct { - state protoimpl.MessageState `protogen:"open.v1"` - UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` // User id (PAT) - PatId string `protobuf:"bytes,2,opt,name=pat_id,json=patId,proto3" json:"pat_id,omitempty"` // Pat id - EntityType uint32 `protobuf:"varint,3,opt,name=entity_type,json=entityType,proto3" json:"entity_type,omitempty"` // Entity type (PAT) - OptionalDomainId string `protobuf:"bytes,4,opt,name=optional_domain_id,json=optionalDomainId,proto3" json:"optional_domain_id,omitempty"` // Optional domain id (PAT) - Operation uint32 `protobuf:"varint,5,opt,name=operation,proto3" json:"operation,omitempty"` // Operation (PAT) - EntityId string `protobuf:"bytes,6,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"` // EntityID (PAT) - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + PatId string `protobuf:"bytes,2,opt,name=pat_id,json=patId,proto3" json:"pat_id,omitempty"` + EntityType uint32 `protobuf:"varint,3,opt,name=entity_type,json=entityType,proto3" json:"entity_type,omitempty"` + DomainId string `protobuf:"bytes,4,opt,name=domain_id,json=domainId,proto3" json:"domain_id,omitempty"` + Operation uint32 `protobuf:"varint,5,opt,name=operation,proto3" json:"operation,omitempty"` + EntityId string `protobuf:"bytes,6,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *PATReq) Reset() { @@ -323,9 +315,9 @@ func (x *PATReq) GetEntityType() uint32 { return 0 } -func (x *PATReq) GetOptionalDomainId() string { +func (x *PATReq) GetDomainId() string { if x != nil { - return x.OptionalDomainId + return x.DomainId } return "" } @@ -345,7 +337,8 @@ func (x *PATReq) GetEntityId() string { } type AuthZReq struct { - state protoimpl.MessageState `protogen:"open.v1"` + state protoimpl.MessageState `protogen:"open.v1"` + TokenType uint32 `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` // Types that are valid to be assigned to AuthType: // // *AuthZReq_Policy @@ -385,6 +378,13 @@ func (*AuthZReq) Descriptor() ([]byte, []int) { return file_auth_v1_auth_proto_rawDescGZIP(), []int{4} } +func (x *AuthZReq) GetTokenType() uint32 { + if x != nil { + return x.TokenType + } + return 0 +} + func (x *AuthZReq) GetAuthType() isAuthZReq_AuthType { if x != nil { return x.AuthType @@ -415,11 +415,11 @@ type isAuthZReq_AuthType interface { } type AuthZReq_Policy struct { - Policy *PolicyReq `protobuf:"bytes,1,opt,name=policy,proto3,oneof"` // Policy-based authorization + Policy *PolicyReq `protobuf:"bytes,2,opt,name=policy,proto3,oneof"` } type AuthZReq_Pat struct { - Pat *PATReq `protobuf:"bytes,2,opt,name=pat,proto3,oneof"` // PAT authorization + Pat *PATReq `protobuf:"bytes,3,opt,name=pat,proto3,oneof"` } func (*AuthZReq_Policy) isAuthZReq_AuthType() {} @@ -491,34 +491,33 @@ const file_auth_v1_auth_proto_rawDesc = "" + "\tuser_role\x18\x03 \x01(\rR\buserRole\x12\x1a\n" + "\bverified\x18\x04 \x01(\bR\bverified\x12\x1d\n" + "\n" + - "token_type\x18\x05 \x01(\rR\ttokenType\"\xc2\x02\n" + - "\tPolicyReq\x12\x1d\n" + + "token_type\x18\x05 \x01(\rR\ttokenType\"\xa3\x02\n" + + "\tPolicyReq\x12\x16\n" + + "\x06domain\x18\x01 \x01(\tR\x06domain\x12!\n" + + "\fsubject_type\x18\x02 \x01(\tR\vsubjectType\x12!\n" + + "\fsubject_kind\x18\x03 \x01(\tR\vsubjectKind\x12)\n" + + "\x10subject_relation\x18\x04 \x01(\tR\x0fsubjectRelation\x12\x18\n" + + "\asubject\x18\x05 \x01(\tR\asubject\x12\x1a\n" + + "\brelation\x18\x06 \x01(\tR\brelation\x12\x1e\n" + "\n" + - "token_type\x18\x01 \x01(\rR\ttokenType\x12\x16\n" + - "\x06domain\x18\x02 \x01(\tR\x06domain\x12!\n" + - "\fsubject_type\x18\x03 \x01(\tR\vsubjectType\x12!\n" + - "\fsubject_kind\x18\x04 \x01(\tR\vsubjectKind\x12)\n" + - "\x10subject_relation\x18\x05 \x01(\tR\x0fsubjectRelation\x12\x18\n" + - "\asubject\x18\x06 \x01(\tR\asubject\x12\x1a\n" + - "\brelation\x18\a \x01(\tR\brelation\x12\x1e\n" + - "\n" + - "permission\x18\b \x01(\tR\n" + + "permission\x18\a \x01(\tR\n" + "permission\x12\x16\n" + - "\x06object\x18\t \x01(\tR\x06object\x12\x1f\n" + - "\vobject_type\x18\n" + - " \x01(\tR\n" + - "objectType\"\xc2\x01\n" + + "\x06object\x18\b \x01(\tR\x06object\x12\x1f\n" + + "\vobject_type\x18\t \x01(\tR\n" + + "objectType\"\xb1\x01\n" + "\x06PATReq\x12\x17\n" + "\auser_id\x18\x01 \x01(\tR\x06userId\x12\x15\n" + "\x06pat_id\x18\x02 \x01(\tR\x05patId\x12\x1f\n" + "\ventity_type\x18\x03 \x01(\rR\n" + - "entityType\x12,\n" + - "\x12optional_domain_id\x18\x04 \x01(\tR\x10optionalDomainId\x12\x1c\n" + + "entityType\x12\x1b\n" + + "\tdomain_id\x18\x04 \x01(\tR\bdomainId\x12\x1c\n" + "\toperation\x18\x05 \x01(\rR\toperation\x12\x1b\n" + - "\tentity_id\x18\x06 \x01(\tR\bentityId\"j\n" + - "\bAuthZReq\x12,\n" + - "\x06policy\x18\x01 \x01(\v2\x12.auth.v1.PolicyReqH\x00R\x06policy\x12#\n" + - "\x03pat\x18\x02 \x01(\v2\x0f.auth.v1.PATReqH\x00R\x03patB\v\n" + + "\tentity_id\x18\x06 \x01(\tR\bentityId\"\x89\x01\n" + + "\bAuthZReq\x12\x1d\n" + + "\n" + + "token_type\x18\x01 \x01(\rR\ttokenType\x12,\n" + + "\x06policy\x18\x02 \x01(\v2\x12.auth.v1.PolicyReqH\x00R\x06policy\x12#\n" + + "\x03pat\x18\x03 \x01(\v2\x0f.auth.v1.PATReqH\x00R\x03patB\v\n" + "\tauth_type\":\n" + "\bAuthZRes\x12\x1e\n" + "\n" + diff --git a/auth/api/grpc/auth/client.go b/auth/api/grpc/auth/client.go index 7f0cbd6d6c..46e0b0b466 100644 --- a/auth/api/grpc/auth/client.go +++ b/auth/api/grpc/auth/client.go @@ -57,7 +57,7 @@ func (client authGrpcClient) Authenticate(ctx context.Context, token *grpcAuthV1 return &grpcAuthV1.AuthNRes{}, grpcapi.DecodeError(err) } ir := res.(authenticateRes) - return &grpcAuthV1.AuthNRes{Id: ir.id, UserId: ir.userID, UserRole: uint32(ir.userRole), Verified: ir.verified}, nil + return &grpcAuthV1.AuthNRes{Id: ir.id, UserId: ir.userID, UserRole: uint32(ir.userRole), Verified: ir.verified, TokenType: ir.tokenType}, nil } func encodeIdentifyRequest(_ context.Context, grpcReq any) (any, error) { @@ -67,7 +67,7 @@ func encodeIdentifyRequest(_ context.Context, grpcReq any) (any, error) { func decodeIdentifyResponse(_ context.Context, grpcRes any) (any, error) { res := grpcRes.(*grpcAuthV1.AuthNRes) - return authenticateRes{id: res.GetId(), userID: res.GetUserId(), userRole: auth.Role(res.UserRole), verified: res.GetVerified()}, nil + return authenticateRes{id: res.GetId(), userID: res.GetUserId(), userRole: auth.Role(res.UserRole), verified: res.GetVerified(), tokenType: res.GetTokenType()}, nil } func (client authGrpcClient) Authorize(ctx context.Context, req *grpcAuthV1.AuthZReq, _ ...grpc.CallOption) (r *grpcAuthV1.AuthZRes, err error) { @@ -78,7 +78,7 @@ func (client authGrpcClient) Authorize(ctx context.Context, req *grpcAuthV1.Auth if policy := req.GetPolicy(); policy != nil { authReqData = authReq{ - TokenType: policy.GetTokenType(), + TokenType: req.GetTokenType(), Domain: policy.GetDomain(), SubjectType: policy.GetSubjectType(), Subject: policy.GetSubject(), @@ -90,12 +90,13 @@ func (client authGrpcClient) Authorize(ctx context.Context, req *grpcAuthV1.Auth } } else if pat := req.GetPat(); pat != nil { authReqData = authReq{ - UserID: pat.GetUserId(), - PatID: pat.GetPatId(), - EntityType: auth.EntityType(pat.GetEntityType()), - OptionalDomainID: pat.GetOptionalDomainId(), - Operation: auth.Operation(pat.GetOperation()), - EntityID: pat.GetEntityId(), + TokenType: req.GetTokenType(), + UserID: pat.GetUserId(), + PatID: pat.GetPatId(), + EntityType: auth.EntityType(pat.GetEntityType()), + DomainID: pat.GetDomainId(), + Operation: auth.Operation(pat.GetOperation()), + EntityID: pat.GetEntityId(), } } @@ -116,27 +117,26 @@ func decodeAuthorizeResponse(_ context.Context, grpcRes any) (any, error) { func encodeAuthorizeRequest(_ context.Context, grpcReq any) (any, error) { req := grpcReq.(authReq) - // Check if this is a PAT request (has PatID) or policy request if req.PatID != "" { return &grpcAuthV1.AuthZReq{ + TokenType: req.TokenType, AuthType: &grpcAuthV1.AuthZReq_Pat{ Pat: &grpcAuthV1.PATReq{ - UserId: req.UserID, - PatId: req.PatID, - EntityType: uint32(req.EntityType), - OptionalDomainId: req.OptionalDomainID, - Operation: uint32(req.Operation), - EntityId: req.EntityID, + UserId: req.UserID, + PatId: req.PatID, + EntityType: uint32(req.EntityType), + DomainId: req.DomainID, + Operation: uint32(req.Operation), + EntityId: req.EntityID, }, }, }, nil } - // Otherwise, it's a policy request return &grpcAuthV1.AuthZReq{ + TokenType: req.TokenType, AuthType: &grpcAuthV1.AuthZReq_Policy{ Policy: &grpcAuthV1.PolicyReq{ - TokenType: req.TokenType, Domain: req.Domain, SubjectType: req.SubjectType, Subject: req.Subject, diff --git a/auth/api/grpc/auth/endpoint.go b/auth/api/grpc/auth/endpoint.go index bfccbc4c3e..b52e16a190 100644 --- a/auth/api/grpc/auth/endpoint.go +++ b/auth/api/grpc/auth/endpoint.go @@ -23,7 +23,12 @@ func authenticateEndpoint(svc auth.Service) endpoint.Endpoint { return authenticateRes{}, err } - return authenticateRes{id: key.ID, userID: key.Subject, userRole: key.Role, verified: key.Verified}, nil + tokenType := auth.AccessTokenType + if key.Type == auth.PersonalAccessToken { + tokenType = auth.PersonalAccessTokenType + } + + return authenticateRes{id: key.ID, userID: key.Subject, userRole: key.Role, verified: key.Verified, tokenType: tokenType}, nil } } @@ -36,21 +41,21 @@ func authorizeEndpoint(svc auth.Service) endpoint.Endpoint { } err := svc.Authorize(ctx, policies.Policy{ - TokenType: req.TokenType, - Domain: req.Domain, - SubjectType: req.SubjectType, - SubjectKind: req.SubjectKind, - Subject: req.Subject, - Relation: req.Relation, - Permission: req.Permission, - ObjectType: req.ObjectType, - Object: req.Object, - UserID: req.UserID, - PatID: req.PatID, - EntityType: uint32(req.EntityType), - OptionalDomainID: req.OptionalDomainID, - Operation: uint32(req.Operation), - EntityID: req.EntityID, + TokenType: req.TokenType, + Domain: req.Domain, + SubjectType: req.SubjectType, + SubjectKind: req.SubjectKind, + Subject: req.Subject, + Relation: req.Relation, + Permission: req.Permission, + ObjectType: req.ObjectType, + Object: req.Object, + UserID: req.UserID, + PatID: req.PatID, + EntityType: uint32(req.EntityType), + DomainID: req.DomainID, + Operation: uint32(req.Operation), + EntityID: req.EntityID, }) if err != nil { return authorizeRes{authorized: false}, err diff --git a/auth/api/grpc/auth/endpoint_test.go b/auth/api/grpc/auth/endpoint_test.go index 4fdb2c136c..4437b9018b 100644 --- a/auth/api/grpc/auth/endpoint_test.go +++ b/auth/api/grpc/auth/endpoint_test.go @@ -15,6 +15,7 @@ import ( "github.com/absmach/supermq/auth" grpcapi "github.com/absmach/supermq/auth/api/grpc/auth" "github.com/absmach/supermq/internal/testsutil" + "github.com/absmach/supermq/pkg/authn" "github.com/absmach/supermq/pkg/errors" svcerr "github.com/absmach/supermq/pkg/errors/service" "github.com/stretchr/testify/assert" @@ -71,7 +72,7 @@ func TestIdentify(t *testing.T) { desc: "authenticate user with valid user token", token: validToken, key: auth.Key{ID: "", Subject: id, Role: auth.UserRole}, - idt: &grpcAuthV1.AuthNRes{UserId: id, UserRole: uint32(auth.UserRole)}, + idt: &grpcAuthV1.AuthNRes{UserId: id, UserRole: uint32(auth.UserRole), TokenType: auth.AccessTokenType}, err: nil, }, { @@ -92,7 +93,7 @@ func TestIdentify(t *testing.T) { desc: "authenticate user with valid PAT token", token: "pat_" + validPATToken, key: auth.Key{ID: id, Type: auth.PersonalAccessToken, Subject: clientID, Role: auth.UserRole}, - idt: &grpcAuthV1.AuthNRes{Id: id, UserId: clientID, UserRole: uint32(auth.UserRole)}, + idt: &grpcAuthV1.AuthNRes{Id: id, UserId: clientID, UserRole: uint32(auth.UserRole), TokenType: auth.PersonalAccessTokenType}, err: nil, }, { @@ -136,6 +137,7 @@ func TestAuthorize(t *testing.T) { desc: "authorize user with authorized token", token: validToken, authRequest: &grpcAuthV1.AuthZReq{ + TokenType: uint32(authn.AccessToken), AuthType: &grpcAuthV1.AuthZReq_Policy{ Policy: &grpcAuthV1.PolicyReq{ Subject: id, @@ -154,6 +156,7 @@ func TestAuthorize(t *testing.T) { desc: "authorize user with unauthorized token", token: inValidToken, authRequest: &grpcAuthV1.AuthZReq{ + TokenType: uint32(authn.AccessToken), AuthType: &grpcAuthV1.AuthZReq_Policy{ Policy: &grpcAuthV1.PolicyReq{ Subject: id, @@ -172,6 +175,7 @@ func TestAuthorize(t *testing.T) { desc: "authorize user with empty subject", token: validToken, authRequest: &grpcAuthV1.AuthZReq{ + TokenType: uint32(authn.AccessToken), AuthType: &grpcAuthV1.AuthZReq_Policy{ Policy: &grpcAuthV1.PolicyReq{ Subject: "", @@ -190,6 +194,7 @@ func TestAuthorize(t *testing.T) { desc: "authorize user with empty subject type", token: validToken, authRequest: &grpcAuthV1.AuthZReq{ + TokenType: uint32(authn.AccessToken), AuthType: &grpcAuthV1.AuthZReq_Policy{ Policy: &grpcAuthV1.PolicyReq{ Subject: id, @@ -208,6 +213,7 @@ func TestAuthorize(t *testing.T) { desc: "authorize user with empty object", token: validToken, authRequest: &grpcAuthV1.AuthZReq{ + TokenType: uint32(authn.AccessToken), AuthType: &grpcAuthV1.AuthZReq_Policy{ Policy: &grpcAuthV1.PolicyReq{ Subject: id, @@ -226,6 +232,7 @@ func TestAuthorize(t *testing.T) { desc: "authorize user with empty object type", token: validToken, authRequest: &grpcAuthV1.AuthZReq{ + TokenType: uint32(authn.AccessToken), AuthType: &grpcAuthV1.AuthZReq_Policy{ Policy: &grpcAuthV1.PolicyReq{ Subject: id, @@ -244,6 +251,7 @@ func TestAuthorize(t *testing.T) { desc: "authorize user with empty permission", token: validToken, authRequest: &grpcAuthV1.AuthZReq{ + TokenType: uint32(authn.AccessToken), AuthType: &grpcAuthV1.AuthZReq_Policy{ Policy: &grpcAuthV1.PolicyReq{ Subject: id, @@ -262,14 +270,15 @@ func TestAuthorize(t *testing.T) { desc: "authorize user with valid PAT token", token: validPATToken, authRequest: &grpcAuthV1.AuthZReq{ + TokenType: uint32(authn.PersonalAccessToken), AuthType: &grpcAuthV1.AuthZReq_Pat{ Pat: &grpcAuthV1.PATReq{ - UserId: id, - PatId: id, - EntityType: uint32(auth.ClientsType), - OptionalDomainId: domainID, - Operation: uint32(auth.CreateOp), - EntityId: clientID, + UserId: id, + PatId: id, + EntityType: uint32(auth.ClientsType), + DomainId: domainID, + Operation: uint32(auth.ClientCreateOp), + EntityId: clientID, }, }, }, @@ -280,14 +289,15 @@ func TestAuthorize(t *testing.T) { desc: "authorize user with unauthorized PAT token", token: inValidToken, authRequest: &grpcAuthV1.AuthZReq{ + TokenType: uint32(authn.PersonalAccessToken), AuthType: &grpcAuthV1.AuthZReq_Pat{ Pat: &grpcAuthV1.PATReq{ - UserId: id, - PatId: id, - EntityType: uint32(auth.ClientsType), - OptionalDomainId: domainID, - Operation: uint32(auth.CreateOp), - EntityId: clientID, + UserId: id, + PatId: id, + EntityType: uint32(auth.ClientsType), + DomainId: domainID, + Operation: uint32(auth.ClientCreateOp), + EntityId: clientID, }, }, }, @@ -298,13 +308,14 @@ func TestAuthorize(t *testing.T) { desc: "authorize PAT with missing user id", token: validPATToken, authRequest: &grpcAuthV1.AuthZReq{ + TokenType: uint32(authn.PersonalAccessToken), AuthType: &grpcAuthV1.AuthZReq_Pat{ Pat: &grpcAuthV1.PATReq{ - PatId: id, - EntityType: uint32(auth.ClientsType), - OptionalDomainId: domainID, - Operation: uint32(auth.CreateOp), - EntityId: clientID, + PatId: id, + EntityType: uint32(auth.ClientsType), + DomainId: domainID, + Operation: uint32(auth.ClientCreateOp), + EntityId: clientID, }, }, }, @@ -315,13 +326,14 @@ func TestAuthorize(t *testing.T) { desc: "authorize PAT with missing entity id", token: validPATToken, authRequest: &grpcAuthV1.AuthZReq{ + TokenType: uint32(authn.PersonalAccessToken), AuthType: &grpcAuthV1.AuthZReq_Pat{ Pat: &grpcAuthV1.PATReq{ - UserId: id, - PatId: id, - EntityType: uint32(auth.ClientsType), - OptionalDomainId: domainID, - Operation: uint32(auth.CreateOp), + UserId: id, + PatId: id, + EntityType: uint32(auth.ClientsType), + DomainId: domainID, + Operation: uint32(auth.ClientCreateOp), }, }, }, diff --git a/auth/api/grpc/auth/requests.go b/auth/api/grpc/auth/requests.go index 6aa4bade0c..29a8cf62a7 100644 --- a/auth/api/grpc/auth/requests.go +++ b/auth/api/grpc/auth/requests.go @@ -36,12 +36,12 @@ type authReq struct { Object string // PAT authorization fields - UserID string - PatID string - EntityType auth.EntityType - OptionalDomainID string - Operation auth.Operation - EntityID string + UserID string + PatID string + EntityType auth.EntityType + DomainID string + Operation auth.Operation + EntityID string } func (req authReq) validate() error { diff --git a/auth/api/grpc/auth/responses.go b/auth/api/grpc/auth/responses.go index 861bc5b890..2e31d44d65 100644 --- a/auth/api/grpc/auth/responses.go +++ b/auth/api/grpc/auth/responses.go @@ -6,10 +6,11 @@ package auth import smqauth "github.com/absmach/supermq/auth" type authenticateRes struct { - id string - userID string - userRole smqauth.Role - verified bool + id string + userID string + userRole smqauth.Role + verified bool + tokenType uint32 } type authorizeRes struct { diff --git a/auth/api/grpc/auth/server.go b/auth/api/grpc/auth/server.go index 992b8331d8..33e360be67 100644 --- a/auth/api/grpc/auth/server.go +++ b/auth/api/grpc/auth/server.go @@ -60,14 +60,14 @@ func decodeAuthenticateRequest(_ context.Context, grpcReq any) (any, error) { func encodeAuthenticateResponse(_ context.Context, grpcRes any) (any, error) { res := grpcRes.(authenticateRes) - return &grpcAuthV1.AuthNRes{Id: res.id, UserId: res.userID, UserRole: uint32(res.userRole), Verified: res.verified}, nil + return &grpcAuthV1.AuthNRes{Id: res.id, UserId: res.userID, UserRole: uint32(res.userRole), Verified: res.verified, TokenType: res.tokenType}, nil } func decodeAuthorizeRequest(_ context.Context, grpcReq any) (any, error) { req := grpcReq.(*grpcAuthV1.AuthZReq) if policy := req.GetPolicy(); policy != nil { return authReq{ - TokenType: policy.GetTokenType(), + TokenType: req.GetTokenType(), Domain: policy.GetDomain(), SubjectType: policy.GetSubjectType(), SubjectKind: policy.GetSubjectKind(), @@ -80,12 +80,13 @@ func decodeAuthorizeRequest(_ context.Context, grpcReq any) (any, error) { } if pat := req.GetPat(); pat != nil { return authReq{ - UserID: pat.GetUserId(), - PatID: pat.GetPatId(), - EntityType: auth.EntityType(pat.GetEntityType()), - OptionalDomainID: pat.GetOptionalDomainId(), - Operation: auth.Operation(pat.GetOperation()), - EntityID: pat.GetEntityId(), + TokenType: req.GetTokenType(), + UserID: pat.GetUserId(), + PatID: pat.GetPatId(), + EntityType: auth.EntityType(pat.GetEntityType()), + DomainID: pat.GetDomainId(), + Operation: auth.Operation(pat.GetOperation()), + EntityID: pat.GetEntityId(), }, nil } diff --git a/auth/api/http/pats/requests_test.go b/auth/api/http/pats/requests_test.go index ab7bb26b1b..0ee0393b4e 100644 --- a/auth/api/http/pats/requests_test.go +++ b/auth/api/http/pats/requests_test.go @@ -508,17 +508,17 @@ func TestClearAllPATReqValidate(t *testing.T) { func TestAddScopeReqValidate(t *testing.T) { validScope := auth.Scope{ - OptionalDomainID: "domain1", - EntityType: auth.GroupsType, - EntityID: "entity1", - Operation: auth.CreateOp, + DomainID: "domain1", + EntityType: auth.GroupsType, + EntityID: "entity1", + Operation: auth.GroupCreateOp, } invalidScope := auth.Scope{ - OptionalDomainID: "", - EntityType: auth.GroupsType, - EntityID: "", - Operation: auth.CreateOp, + DomainID: "", + EntityType: auth.GroupsType, + EntityID: "", + Operation: auth.GroupCreateOp, } cases := []struct { diff --git a/auth/cache/pat.go b/auth/cache/pat.go index 53002e7237..b12e1519e3 100644 --- a/auth/cache/pat.go +++ b/auth/cache/pat.go @@ -28,7 +28,7 @@ func NewPatsCache(client *redis.Client, duration time.Duration) auth.Cache { func (pc *patCache) Save(ctx context.Context, userID string, scopes []auth.Scope) error { for _, sc := range scopes { - key := generateKey(userID, sc.PatID, sc.OptionalDomainID, sc.EntityType, sc.Operation, sc.EntityID) + key := generateKey(userID, sc.PatID, sc.DomainID, sc.EntityType, sc.Operation, sc.EntityID) if err := pc.client.Set(ctx, key, sc.ID, pc.duration).Err(); err != nil { return errors.Wrap(repoerr.ErrCreateEntity, err) } @@ -37,9 +37,9 @@ func (pc *patCache) Save(ctx context.Context, userID string, scopes []auth.Scope return nil } -func (pc *patCache) CheckScope(ctx context.Context, userID, patID, optionalDomainID string, entityType auth.EntityType, operation auth.Operation, entityID string) bool { - exactKey := fmt.Sprintf("pat:%s:%s:%s:%s:%s:%s", userID, patID, entityType, optionalDomainID, operation, entityID) - wildcardKey := fmt.Sprintf("pat:%s:%s:%s:%s:%s:*", userID, patID, entityType, operation, operation) +func (pc *patCache) CheckScope(ctx context.Context, userID, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string) bool { + exactKey := fmt.Sprintf("pat:%s:%s:%s:%s:%s:%s", userID, patID, entityType, domainID, operation, entityID) + wildcardKey := fmt.Sprintf("pat:%s:%s:%s:%s:%s:*", userID, patID, entityType, domainID, operation) res, err := pc.client.Exists(ctx, exactKey, wildcardKey).Result() if err != nil { @@ -115,6 +115,6 @@ func (pc *patCache) RemoveAllScope(ctx context.Context, userID, patID string) er return nil } -func generateKey(userID, patID, optionalDomainId string, entityType auth.EntityType, operation auth.Operation, entityID string) string { - return fmt.Sprintf("pat:%s:%s:%s:%s:%s:%s", userID, patID, entityType, optionalDomainId, operation, entityID) +func generateKey(userID, patID, domainId string, entityType auth.EntityType, operation auth.Operation, entityID string) string { + return fmt.Sprintf("pat:%s:%s:%s:%s:%s:%s", userID, patID, entityType, domainId, operation, entityID) } diff --git a/auth/middleware/logging.go b/auth/middleware/logging.go index b2b8df685b..70dd756b43 100644 --- a/auth/middleware/logging.go +++ b/auth/middleware/logging.go @@ -307,7 +307,7 @@ func (lm *loggingMiddleware) AddScope(ctx context.Context, token, patID string, var groupArgs []any for _, s := range scopes { groupArgs = append(groupArgs, slog.String("entity_type", s.EntityType.String())) - groupArgs = append(groupArgs, slog.String("optional_domain_id", s.OptionalDomainID)) + groupArgs = append(groupArgs, slog.String("domain_id", s.DomainID)) groupArgs = append(groupArgs, slog.String("operation", s.Operation.String())) groupArgs = append(groupArgs, slog.String("entity_id", s.EntityID)) } @@ -379,12 +379,12 @@ func (lm *loggingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (p return lm.svc.IdentifyPAT(ctx, paToken) } -func (lm *loggingMiddleware) AuthorizePAT(ctx context.Context, userID, patID string, entityType auth.EntityType, optionalDomainID string, operation auth.Operation, entityID string) (err error) { +func (lm *loggingMiddleware) AuthorizePAT(ctx context.Context, userID, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string) (err error) { defer func(begin time.Time) { args := []any{ slog.String("duration", time.Since(begin).String()), slog.String("entity_type", entityType.String()), - slog.String("optional_domain_id", optionalDomainID), + slog.String("domain_id", domainID), slog.String("operation", operation.String()), slog.String("entities", entityID), } @@ -395,5 +395,5 @@ func (lm *loggingMiddleware) AuthorizePAT(ctx context.Context, userID, patID str } lm.logger.Info("Authorize PAT completed successfully", args...) }(time.Now()) - return lm.svc.AuthorizePAT(ctx, userID, patID, entityType, optionalDomainID, operation, entityID) + return lm.svc.AuthorizePAT(ctx, userID, patID, entityType, domainID, operation, entityID) } diff --git a/auth/middleware/metrics.go b/auth/middleware/metrics.go index e8c2560bc0..54deba1b25 100644 --- a/auth/middleware/metrics.go +++ b/auth/middleware/metrics.go @@ -195,10 +195,10 @@ func (ms *metricsMiddleware) IdentifyPAT(ctx context.Context, paToken string) (a return ms.svc.IdentifyPAT(ctx, paToken) } -func (ms *metricsMiddleware) AuthorizePAT(ctx context.Context, userID, patID string, entityType auth.EntityType, optionalDomainID string, operation auth.Operation, entityID string) error { +func (ms *metricsMiddleware) AuthorizePAT(ctx context.Context, userID, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string) error { defer func(begin time.Time) { ms.counter.With("method", "authorize_pat").Add(1) ms.latency.With("method", "authorize_pat").Observe(time.Since(begin).Seconds()) }(time.Now()) - return ms.svc.AuthorizePAT(ctx, userID, patID, entityType, optionalDomainID, operation, entityID) + return ms.svc.AuthorizePAT(ctx, userID, patID, entityType, domainID, operation, entityID) } diff --git a/auth/middleware/tracing.go b/auth/middleware/tracing.go index 3855b1b1ff..ef1d9167d7 100644 --- a/auth/middleware/tracing.go +++ b/auth/middleware/tracing.go @@ -169,7 +169,7 @@ func (tm *tracingMiddleware) AddScope(ctx context.Context, token, patID string, var attributes []attribute.KeyValue for _, s := range scopes { attributes = append(attributes, attribute.String("entity_type", s.EntityType.String())) - attributes = append(attributes, attribute.String("optional_domain_id", s.OptionalDomainID)) + attributes = append(attributes, attribute.String("domain_id", s.DomainID)) attributes = append(attributes, attribute.String("operation", s.Operation.String())) attributes = append(attributes, attribute.String("entity_id", s.EntityID)) } @@ -208,14 +208,14 @@ func (tm *tracingMiddleware) IdentifyPAT(ctx context.Context, paToken string) (a return tm.svc.IdentifyPAT(ctx, paToken) } -func (tm *tracingMiddleware) AuthorizePAT(ctx context.Context, userID, patID string, entityType auth.EntityType, optionalDomainID string, operation auth.Operation, entityID string) error { +func (tm *tracingMiddleware) AuthorizePAT(ctx context.Context, userID, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string) error { ctx, span := tm.tracer.Start(ctx, "authorize_pat", trace.WithAttributes( attribute.String("pat_id", patID), attribute.String("entity_type", entityType.String()), - attribute.String("optional_domain_id", optionalDomainID), + attribute.String("domain_id", domainID), attribute.String("operation", operation.String()), attribute.String("entities", entityID), )) defer span.End() - return tm.svc.AuthorizePAT(ctx, userID, patID, entityType, optionalDomainID, operation, entityID) + return tm.svc.AuthorizePAT(ctx, userID, patID, entityType, domainID, operation, entityID) } diff --git a/auth/mocks/cache.go b/auth/mocks/cache.go index 4a40021f79..a6b302c2d5 100644 --- a/auth/mocks/cache.go +++ b/auth/mocks/cache.go @@ -43,16 +43,16 @@ func (_m *Cache) EXPECT() *Cache_Expecter { } // CheckScope provides a mock function for the type Cache -func (_mock *Cache) CheckScope(ctx context.Context, userID string, patID string, optionalDomainID string, entityType auth.EntityType, operation auth.Operation, entityID string) bool { - ret := _mock.Called(ctx, userID, patID, optionalDomainID, entityType, operation, entityID) +func (_mock *Cache) CheckScope(ctx context.Context, userID string, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string) bool { + ret := _mock.Called(ctx, userID, patID, entityType, domainID, operation, entityID) if len(ret) == 0 { panic("no return value specified for CheckScope") } var r0 bool - if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, string, auth.EntityType, auth.Operation, string) bool); ok { - r0 = returnFunc(ctx, userID, patID, optionalDomainID, entityType, operation, entityID) + if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, auth.EntityType, string, auth.Operation, string) bool); ok { + r0 = returnFunc(ctx, userID, patID, entityType, domainID, operation, entityID) } else { r0 = ret.Get(0).(bool) } @@ -68,15 +68,15 @@ type Cache_CheckScope_Call struct { // - ctx context.Context // - userID string // - patID string -// - optionalDomainID string // - entityType auth.EntityType +// - domainID string // - operation auth.Operation // - entityID string -func (_e *Cache_Expecter) CheckScope(ctx interface{}, userID interface{}, patID interface{}, optionalDomainID interface{}, entityType interface{}, operation interface{}, entityID interface{}) *Cache_CheckScope_Call { - return &Cache_CheckScope_Call{Call: _e.mock.On("CheckScope", ctx, userID, patID, optionalDomainID, entityType, operation, entityID)} +func (_e *Cache_Expecter) CheckScope(ctx interface{}, userID interface{}, patID interface{}, entityType interface{}, domainID interface{}, operation interface{}, entityID interface{}) *Cache_CheckScope_Call { + return &Cache_CheckScope_Call{Call: _e.mock.On("CheckScope", ctx, userID, patID, entityType, domainID, operation, entityID)} } -func (_c *Cache_CheckScope_Call) Run(run func(ctx context.Context, userID string, patID string, optionalDomainID string, entityType auth.EntityType, operation auth.Operation, entityID string)) *Cache_CheckScope_Call { +func (_c *Cache_CheckScope_Call) Run(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string)) *Cache_CheckScope_Call { _c.Call.Run(func(args mock.Arguments) { var arg0 context.Context if args[0] != nil { @@ -90,13 +90,13 @@ func (_c *Cache_CheckScope_Call) Run(run func(ctx context.Context, userID string if args[2] != nil { arg2 = args[2].(string) } - var arg3 string + var arg3 auth.EntityType if args[3] != nil { - arg3 = args[3].(string) + arg3 = args[3].(auth.EntityType) } - var arg4 auth.EntityType + var arg4 string if args[4] != nil { - arg4 = args[4].(auth.EntityType) + arg4 = args[4].(string) } var arg5 auth.Operation if args[5] != nil { @@ -124,7 +124,7 @@ func (_c *Cache_CheckScope_Call) Return(b bool) *Cache_CheckScope_Call { return _c } -func (_c *Cache_CheckScope_Call) RunAndReturn(run func(ctx context.Context, userID string, patID string, optionalDomainID string, entityType auth.EntityType, operation auth.Operation, entityID string) bool) *Cache_CheckScope_Call { +func (_c *Cache_CheckScope_Call) RunAndReturn(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string) bool) *Cache_CheckScope_Call { _c.Call.Return(run) return _c } diff --git a/auth/mocks/pats.go b/auth/mocks/pats.go index 752046f281..d0e2b8f36f 100644 --- a/auth/mocks/pats.go +++ b/auth/mocks/pats.go @@ -113,8 +113,8 @@ func (_c *PATS_AddScope_Call) RunAndReturn(run func(ctx context.Context, token s } // AuthorizePAT provides a mock function for the type PATS -func (_mock *PATS) AuthorizePAT(ctx context.Context, userID string, patID string, entityType auth.EntityType, optionalDomainID string, operation auth.Operation, entityID string) error { - ret := _mock.Called(ctx, userID, patID, entityType, optionalDomainID, operation, entityID) +func (_mock *PATS) AuthorizePAT(ctx context.Context, userID string, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string) error { + ret := _mock.Called(ctx, userID, patID, entityType, domainID, operation, entityID) if len(ret) == 0 { panic("no return value specified for AuthorizePAT") @@ -122,7 +122,7 @@ func (_mock *PATS) AuthorizePAT(ctx context.Context, userID string, patID string var r0 error if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, auth.EntityType, string, auth.Operation, string) error); ok { - r0 = returnFunc(ctx, userID, patID, entityType, optionalDomainID, operation, entityID) + r0 = returnFunc(ctx, userID, patID, entityType, domainID, operation, entityID) } else { r0 = ret.Error(0) } @@ -139,14 +139,14 @@ type PATS_AuthorizePAT_Call struct { // - userID string // - patID string // - entityType auth.EntityType -// - optionalDomainID string +// - domainID string // - operation auth.Operation // - entityID string -func (_e *PATS_Expecter) AuthorizePAT(ctx interface{}, userID interface{}, patID interface{}, entityType interface{}, optionalDomainID interface{}, operation interface{}, entityID interface{}) *PATS_AuthorizePAT_Call { - return &PATS_AuthorizePAT_Call{Call: _e.mock.On("AuthorizePAT", ctx, userID, patID, entityType, optionalDomainID, operation, entityID)} +func (_e *PATS_Expecter) AuthorizePAT(ctx interface{}, userID interface{}, patID interface{}, entityType interface{}, domainID interface{}, operation interface{}, entityID interface{}) *PATS_AuthorizePAT_Call { + return &PATS_AuthorizePAT_Call{Call: _e.mock.On("AuthorizePAT", ctx, userID, patID, entityType, domainID, operation, entityID)} } -func (_c *PATS_AuthorizePAT_Call) Run(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, optionalDomainID string, operation auth.Operation, entityID string)) *PATS_AuthorizePAT_Call { +func (_c *PATS_AuthorizePAT_Call) Run(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string)) *PATS_AuthorizePAT_Call { _c.Call.Run(func(args mock.Arguments) { var arg0 context.Context if args[0] != nil { @@ -194,7 +194,7 @@ func (_c *PATS_AuthorizePAT_Call) Return(err error) *PATS_AuthorizePAT_Call { return _c } -func (_c *PATS_AuthorizePAT_Call) RunAndReturn(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, optionalDomainID string, operation auth.Operation, entityID string) error) *PATS_AuthorizePAT_Call { +func (_c *PATS_AuthorizePAT_Call) RunAndReturn(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string) error) *PATS_AuthorizePAT_Call { _c.Call.Return(run) return _c } diff --git a/auth/mocks/pats_repository.go b/auth/mocks/pats_repository.go index 87cff1db85..2c4871a594 100644 --- a/auth/mocks/pats_repository.go +++ b/auth/mocks/pats_repository.go @@ -107,8 +107,8 @@ func (_c *PATSRepository_AddScope_Call) RunAndReturn(run func(ctx context.Contex } // CheckScope provides a mock function for the type PATSRepository -func (_mock *PATSRepository) CheckScope(ctx context.Context, userID string, patID string, entityType auth.EntityType, optionalDomainID string, operation auth.Operation, entityID string) error { - ret := _mock.Called(ctx, userID, patID, entityType, optionalDomainID, operation, entityID) +func (_mock *PATSRepository) CheckScope(ctx context.Context, userID string, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string) error { + ret := _mock.Called(ctx, userID, patID, entityType, domainID, operation, entityID) if len(ret) == 0 { panic("no return value specified for CheckScope") @@ -116,7 +116,7 @@ func (_mock *PATSRepository) CheckScope(ctx context.Context, userID string, patI var r0 error if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, auth.EntityType, string, auth.Operation, string) error); ok { - r0 = returnFunc(ctx, userID, patID, entityType, optionalDomainID, operation, entityID) + r0 = returnFunc(ctx, userID, patID, entityType, domainID, operation, entityID) } else { r0 = ret.Error(0) } @@ -133,14 +133,14 @@ type PATSRepository_CheckScope_Call struct { // - userID string // - patID string // - entityType auth.EntityType -// - optionalDomainID string +// - domainID string // - operation auth.Operation // - entityID string -func (_e *PATSRepository_Expecter) CheckScope(ctx interface{}, userID interface{}, patID interface{}, entityType interface{}, optionalDomainID interface{}, operation interface{}, entityID interface{}) *PATSRepository_CheckScope_Call { - return &PATSRepository_CheckScope_Call{Call: _e.mock.On("CheckScope", ctx, userID, patID, entityType, optionalDomainID, operation, entityID)} +func (_e *PATSRepository_Expecter) CheckScope(ctx interface{}, userID interface{}, patID interface{}, entityType interface{}, domainID interface{}, operation interface{}, entityID interface{}) *PATSRepository_CheckScope_Call { + return &PATSRepository_CheckScope_Call{Call: _e.mock.On("CheckScope", ctx, userID, patID, entityType, domainID, operation, entityID)} } -func (_c *PATSRepository_CheckScope_Call) Run(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, optionalDomainID string, operation auth.Operation, entityID string)) *PATSRepository_CheckScope_Call { +func (_c *PATSRepository_CheckScope_Call) Run(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string)) *PATSRepository_CheckScope_Call { _c.Call.Run(func(args mock.Arguments) { var arg0 context.Context if args[0] != nil { @@ -188,7 +188,7 @@ func (_c *PATSRepository_CheckScope_Call) Return(err error) *PATSRepository_Chec return _c } -func (_c *PATSRepository_CheckScope_Call) RunAndReturn(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, optionalDomainID string, operation auth.Operation, entityID string) error) *PATSRepository_CheckScope_Call { +func (_c *PATSRepository_CheckScope_Call) RunAndReturn(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string) error) *PATSRepository_CheckScope_Call { _c.Call.Return(run) return _c } diff --git a/auth/mocks/service.go b/auth/mocks/service.go index 4482b6df73..ecea3485dc 100644 --- a/auth/mocks/service.go +++ b/auth/mocks/service.go @@ -171,8 +171,8 @@ func (_c *Service_Authorize_Call) RunAndReturn(run func(ctx context.Context, pr } // AuthorizePAT provides a mock function for the type Service -func (_mock *Service) AuthorizePAT(ctx context.Context, userID string, patID string, entityType auth.EntityType, optionalDomainID string, operation auth.Operation, entityID string) error { - ret := _mock.Called(ctx, userID, patID, entityType, optionalDomainID, operation, entityID) +func (_mock *Service) AuthorizePAT(ctx context.Context, userID string, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string) error { + ret := _mock.Called(ctx, userID, patID, entityType, domainID, operation, entityID) if len(ret) == 0 { panic("no return value specified for AuthorizePAT") @@ -180,7 +180,7 @@ func (_mock *Service) AuthorizePAT(ctx context.Context, userID string, patID str var r0 error if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, auth.EntityType, string, auth.Operation, string) error); ok { - r0 = returnFunc(ctx, userID, patID, entityType, optionalDomainID, operation, entityID) + r0 = returnFunc(ctx, userID, patID, entityType, domainID, operation, entityID) } else { r0 = ret.Error(0) } @@ -197,14 +197,14 @@ type Service_AuthorizePAT_Call struct { // - userID string // - patID string // - entityType auth.EntityType -// - optionalDomainID string +// - domainID string // - operation auth.Operation // - entityID string -func (_e *Service_Expecter) AuthorizePAT(ctx interface{}, userID interface{}, patID interface{}, entityType interface{}, optionalDomainID interface{}, operation interface{}, entityID interface{}) *Service_AuthorizePAT_Call { - return &Service_AuthorizePAT_Call{Call: _e.mock.On("AuthorizePAT", ctx, userID, patID, entityType, optionalDomainID, operation, entityID)} +func (_e *Service_Expecter) AuthorizePAT(ctx interface{}, userID interface{}, patID interface{}, entityType interface{}, domainID interface{}, operation interface{}, entityID interface{}) *Service_AuthorizePAT_Call { + return &Service_AuthorizePAT_Call{Call: _e.mock.On("AuthorizePAT", ctx, userID, patID, entityType, domainID, operation, entityID)} } -func (_c *Service_AuthorizePAT_Call) Run(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, optionalDomainID string, operation auth.Operation, entityID string)) *Service_AuthorizePAT_Call { +func (_c *Service_AuthorizePAT_Call) Run(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string)) *Service_AuthorizePAT_Call { _c.Call.Run(func(args mock.Arguments) { var arg0 context.Context if args[0] != nil { @@ -252,7 +252,7 @@ func (_c *Service_AuthorizePAT_Call) Return(err error) *Service_AuthorizePAT_Cal return _c } -func (_c *Service_AuthorizePAT_Call) RunAndReturn(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, optionalDomainID string, operation auth.Operation, entityID string) error) *Service_AuthorizePAT_Call { +func (_c *Service_AuthorizePAT_Call) RunAndReturn(run func(ctx context.Context, userID string, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string) error) *Service_AuthorizePAT_Call { _c.Call.Return(run) return _c } diff --git a/auth/pat.go b/auth/pat.go index dcc8dc21d9..f7576b2f70 100644 --- a/auth/pat.go +++ b/auth/pat.go @@ -16,88 +16,198 @@ import ( const AnyIDs = "*" +var errInvalidEntityOp = errors.NewRequestError("operation not valid for entity type") + type Operation uint32 +// Clients operations. const ( - CreateOp Operation = iota - ReadOp - ListOp - UpdateOp - DeleteOp - ShareOp - UnshareOp - PublishOp - SubscribeOp + ClientCreateOp Operation = iota + 100 + ClientListOp + ClientViewOp + ClientUpdateOp + ClientUpdateTagsOp + ClientUpdateSecretOp + ClientEnableOp + ClientDisableOp + ClientDeleteOp + ClientSetParentGroupOp + ClientRemoveParentGroupOp + ClientConnectToChannelOp + ClientDisconnectFromChannelOp ) +// Channels operations. const ( - createOpStr = "create" - readOpStr = "read" - listOpStr = "list" - updateOpStr = "update" - deleteOpStr = "delete" - shareOpStr = "share" - UnshareOpStr = "unshare" - PublishOpStr = "publish" - SubscribeOpStr = "subscribe" + ChannelCreateOp Operation = iota + 200 + ChannelListOp + ChannelViewOp + ChannelUpdateOp + ChannelUpdateTagsOp + ChannelEnableOp + ChannelDisableOp + ChannelDeleteOp + ChannelSetParentGroupOp + ChannelRemoveParentGroupOp + ChannelConnectToClientOp + ChannelDisconnectFromClientOp ) +// Groups operations. +const ( + GroupCreateOp Operation = iota + 300 + GroupListOp + GroupViewOp + GroupUpdateOp + GroupUpdateTagsOp + GroupEnableOp + GroupDisableOp + GroupDeleteOp + GroupRetrieveHierarchyOp + GroupAddParentGroupOp + GroupRemoveParentGroupOp + GroupAddChildrenGroupsOp + GroupRemoveChildrenGroupsOp + GroupRemoveAllChildrenGroupsOp + GroupListChildrenGroupsOp + GroupSetChildClientOp + GroupRemoveChildClientOp + GroupSetChildChannelOp + GroupRemoveChildChannelOp +) + +// Dashboard operations. +const ( + DashboardShareOp Operation = iota + 400 + DashboardUnshareOp +) + +// Messages operations. +const ( + MessagePublishOp Operation = iota + 500 + MessageSubscribeOp +) + +// Role operations - common for clients, channels, and groups. +const ( + RoleAddOp Operation = iota + 600 + RoleRemoveOp + RoleUpdateOp + RoleRetrieveOp + RoleRetrieveAllOp + RoleAddActionsOp + RoleListActionsOp + RoleCheckActionsExistsOp + RoleRemoveActionsOp + RoleRemoveAllActionsOp + RoleAddMembersOp + RoleListMembersOp + RoleCheckMembersExistsOp + RoleRemoveMembersOp + RoleRemoveAllMembersOp +) + +// operationToString maps Operation values to their string representation. +var operationToString = map[Operation]string{ + // Client operations + ClientCreateOp: "client_create", + ClientListOp: "client_list", + ClientViewOp: "client_view", + ClientUpdateOp: "client_update", + ClientUpdateTagsOp: "client_update_tags", + ClientUpdateSecretOp: "client_update_secret", + ClientEnableOp: "client_enable", + ClientDisableOp: "client_disable", + ClientDeleteOp: "client_delete", + ClientSetParentGroupOp: "client_set_parent_group", + ClientRemoveParentGroupOp: "client_remove_parent_group", + ClientConnectToChannelOp: "client_connect_to_channel", + ClientDisconnectFromChannelOp: "client_disconnect_from_channel", + // Channel operations + ChannelCreateOp: "channel_create", + ChannelListOp: "channel_list", + ChannelViewOp: "channel_view", + ChannelUpdateOp: "channel_update", + ChannelUpdateTagsOp: "channel_update_tags", + ChannelEnableOp: "channel_enable", + ChannelDisableOp: "channel_disable", + ChannelDeleteOp: "channel_delete", + ChannelSetParentGroupOp: "channel_set_parent_group", + ChannelRemoveParentGroupOp: "channel_remove_parent_group", + ChannelConnectToClientOp: "channel_connect_to_client", + ChannelDisconnectFromClientOp: "channel_disconnect_from_client", + // Group operations + GroupCreateOp: "group_create", + GroupListOp: "group_list", + GroupViewOp: "group_view", + GroupUpdateOp: "group_update", + GroupUpdateTagsOp: "group_update_tags", + GroupEnableOp: "group_enable", + GroupDisableOp: "group_disable", + GroupDeleteOp: "group_delete", + GroupRetrieveHierarchyOp: "group_retrieve_hierarchy", + GroupAddParentGroupOp: "group_add_parent_group", + GroupRemoveParentGroupOp: "group_remove_parent_group", + GroupAddChildrenGroupsOp: "group_add_children_groups", + GroupRemoveChildrenGroupsOp: "group_remove_children_groups", + GroupRemoveAllChildrenGroupsOp: "group_remove_all_children_groups", + GroupListChildrenGroupsOp: "group_list_children_groups", + GroupSetChildClientOp: "group_set_child_client", + GroupRemoveChildClientOp: "group_remove_child_client", + GroupSetChildChannelOp: "group_set_child_channel", + GroupRemoveChildChannelOp: "group_remove_child_channel", + // Dashboard operations + DashboardShareOp: "dashboard_share", + DashboardUnshareOp: "dashboard_unshare", + // Message operations + MessagePublishOp: "message_publish", + MessageSubscribeOp: "message_subscribe", + // Role operations - common for clients, channels, and groups + RoleAddOp: "role_add", + RoleRemoveOp: "role_remove", + RoleUpdateOp: "role_update", + RoleRetrieveOp: "role_retrieve", + RoleRetrieveAllOp: "role_retrieve_all", + RoleAddActionsOp: "role_add_actions", + RoleListActionsOp: "role_list_actions", + RoleCheckActionsExistsOp: "role_check_actions_exists", + RoleRemoveActionsOp: "role_remove_actions", + RoleRemoveAllActionsOp: "role_remove_all_actions", + RoleAddMembersOp: "role_add_members", + RoleListMembersOp: "role_list_members", + RoleCheckMembersExistsOp: "role_check_members_exists", + RoleRemoveMembersOp: "role_remove_members", + RoleRemoveAllMembersOp: "role_remove_all_members", +} + +// stringToOperation is the reverse map, built from operationToString. +var stringToOperation = func() map[string]Operation { + m := make(map[string]Operation) + for op, str := range operationToString { + m[str] = op + } + return m +}() + func (op Operation) String() string { - switch op { - case CreateOp: - return createOpStr - case ReadOp: - return readOpStr - case ListOp: - return listOpStr - case UpdateOp: - return updateOpStr - case DeleteOp: - return deleteOpStr - case ShareOp: - return shareOpStr - case UnshareOp: - return UnshareOpStr - case PublishOp: - return PublishOpStr - case SubscribeOp: - return SubscribeOpStr - default: - return fmt.Sprintf("unknown operation type %d", op) + if str, ok := operationToString[op]; ok { + return str } + return fmt.Sprintf("unknown operation type %d", op) } func (op Operation) ValidString() (string, error) { - str := op.String() - if str == fmt.Sprintf("unknown operation type %d", op) { - return "", errors.New(str) + if str, ok := operationToString[op]; ok { + return str, nil } - return str, nil + return "", fmt.Errorf("unknown operation type %d", op) } func ParseOperation(op string) (Operation, error) { - switch op { - case createOpStr: - return CreateOp, nil - case readOpStr: - return ReadOp, nil - case listOpStr: - return ListOp, nil - case updateOpStr: - return UpdateOp, nil - case deleteOpStr: - return DeleteOp, nil - case shareOpStr: - return ShareOp, nil - case UnshareOpStr: - return UnshareOp, nil - case PublishOpStr: - return PublishOp, nil - case SubscribeOpStr: - return SubscribeOp, nil - default: - return 0, fmt.Errorf("unknown operation type %s", op) + if operation, ok := stringToOperation[op]; ok { + return operation, nil } + return 0, fmt.Errorf("unknown operation type %s", op) } func (op Operation) MarshalJSON() ([]byte, error) { @@ -127,8 +237,6 @@ const ( GroupsType EntityType = iota ChannelsType ClientsType - DomainsType - UsersType DashboardType MessagesType ) @@ -137,8 +245,6 @@ const ( GroupsScopeStr = "groups" ChannelsScopeStr = "channels" ClientsScopeStr = "clients" - DomainsStr = "domains" - UsersStr = "users" DashboardsStr = "dashboards" MessagesStr = "messages" ) @@ -151,10 +257,6 @@ func (et EntityType) String() string { return ChannelsScopeStr case ClientsType: return ClientsScopeStr - case DomainsType: - return DomainsStr - case UsersType: - return UsersStr case DashboardType: return DashboardsStr case MessagesType: @@ -180,10 +282,6 @@ func ParseEntityType(et string) (EntityType, error) { return ChannelsType, nil case ClientsScopeStr: return ClientsType, nil - case DomainsStr: - return DomainsType, nil - case UsersStr: - return UsersType, nil case DashboardsStr: return DashboardType, nil case MessagesStr: @@ -214,39 +312,159 @@ func (et *EntityType) UnmarshalText(data []byte) (err error) { return err } +var ValidOperationsForEntity = map[EntityType][]Operation{ + ClientsType: { + ClientCreateOp, + ClientListOp, + ClientViewOp, + ClientUpdateOp, + ClientUpdateTagsOp, + ClientUpdateSecretOp, + ClientEnableOp, + ClientDisableOp, + ClientDeleteOp, + ClientSetParentGroupOp, + ClientRemoveParentGroupOp, + ClientConnectToChannelOp, + ClientDisconnectFromChannelOp, + RoleAddOp, + RoleRemoveOp, + RoleUpdateOp, + RoleRetrieveOp, + RoleRetrieveAllOp, + RoleAddActionsOp, + RoleListActionsOp, + RoleCheckActionsExistsOp, + RoleRemoveActionsOp, + RoleRemoveAllActionsOp, + RoleAddMembersOp, + RoleListMembersOp, + RoleCheckMembersExistsOp, + RoleRemoveMembersOp, + RoleRemoveAllMembersOp, + }, + ChannelsType: { + ChannelCreateOp, + ChannelListOp, + ChannelViewOp, + ChannelUpdateOp, + ChannelUpdateTagsOp, + ChannelEnableOp, + ChannelDisableOp, + ChannelDeleteOp, + ChannelSetParentGroupOp, + ChannelRemoveParentGroupOp, + ChannelConnectToClientOp, + ChannelDisconnectFromClientOp, + RoleAddOp, + RoleRemoveOp, + RoleUpdateOp, + RoleRetrieveOp, + RoleRetrieveAllOp, + RoleAddActionsOp, + RoleListActionsOp, + RoleCheckActionsExistsOp, + RoleRemoveActionsOp, + RoleRemoveAllActionsOp, + RoleAddMembersOp, + RoleListMembersOp, + RoleCheckMembersExistsOp, + RoleRemoveMembersOp, + RoleRemoveAllMembersOp, + }, + GroupsType: { + GroupCreateOp, + GroupListOp, + GroupViewOp, + GroupUpdateOp, + GroupUpdateTagsOp, + GroupEnableOp, + GroupDisableOp, + GroupDeleteOp, + GroupRetrieveHierarchyOp, + GroupAddParentGroupOp, + GroupRemoveParentGroupOp, + GroupAddChildrenGroupsOp, + GroupRemoveChildrenGroupsOp, + GroupRemoveAllChildrenGroupsOp, + GroupListChildrenGroupsOp, + GroupSetChildClientOp, + GroupRemoveChildClientOp, + GroupSetChildChannelOp, + GroupRemoveChildChannelOp, + RoleAddOp, + RoleRemoveOp, + RoleUpdateOp, + RoleRetrieveOp, + RoleRetrieveAllOp, + RoleAddActionsOp, + RoleListActionsOp, + RoleCheckActionsExistsOp, + RoleRemoveActionsOp, + RoleRemoveAllActionsOp, + RoleAddMembersOp, + RoleListMembersOp, + RoleCheckMembersExistsOp, + RoleRemoveMembersOp, + RoleRemoveAllMembersOp, + }, + DashboardType: { + DashboardShareOp, + DashboardUnshareOp, + }, + MessagesType: { + MessagePublishOp, + MessageSubscribeOp, + }, +} + +// IsValidOperationForEntity checks if the given operation is valid for the entity type. +func IsValidOperationForEntity(entityType EntityType, operation Operation) bool { + validOps, exists := ValidOperationsForEntity[entityType] + if !exists { + return false + } + for _, op := range validOps { + if op == operation { + return true + } + } + return false +} + // Example Scope as JSON // // [ // { -// "optional_domain_id": "domain_1", +// "domain_id": "domain_1", // "entity_type": "groups", -// "operation": "create", +// "operation": "group_create", // "entity_id": "*" // }, // { -// "optional_domain_id": "domain_1", +// "domain_id": "domain_1", // "entity_type": "channels", -// "operation": "delete", +// "operation": "channel_delete", // "entity_id": "channel1" // }, // { -// "optional_domain_id": "domain_1", -// "entity_type": "things", -// "operation": "update", +// "domain_id": "domain_1", +// "entity_type": "clients", +// "operation": "client_update", // "entity_id": "*" // } // ] type Scope struct { - ID string `json:"id"` - PatID string `json:"pat_id"` - OptionalDomainID string `json:"optional_domain_id"` - EntityType EntityType `json:"entity_type"` - EntityID string `json:"entity_id"` - Operation Operation `json:"operation"` + ID string `json:"id"` + PatID string `json:"pat_id"` + DomainID string `json:"domain_id"` + EntityType EntityType `json:"entity_type"` + EntityID string `json:"entity_id"` + Operation Operation `json:"operation"` } -func (s *Scope) Authorized(entityType EntityType, optionalDomainID string, operation Operation, entityID string) bool { +func (s *Scope) Authorized(entityType EntityType, domainID string, operation Operation, entityID string) bool { if s == nil { return false } @@ -255,7 +473,7 @@ func (s *Scope) Authorized(entityType EntityType, optionalDomainID string, opera return false } - if optionalDomainID != "" && s.OptionalDomainID != optionalDomainID { + if s.DomainID != "" && s.DomainID != domainID { return false } @@ -281,11 +499,12 @@ func (s *Scope) Validate() error { return apiutil.ErrMissingEntityID } - switch s.EntityType { - case ChannelsType, GroupsType, ClientsType: - if s.OptionalDomainID == "" { - return apiutil.ErrMissingDomainID - } + if s.DomainID == "" { + return apiutil.ErrMissingDomainID + } + + if !IsValidOperationForEntity(s.EntityType, s.Operation) { + return errors.Wrap(apiutil.ErrInvalidQueryParams, errInvalidEntityOp) } return nil @@ -411,7 +630,7 @@ type PATS interface { IdentifyPAT(ctx context.Context, paToken string) (PAT, error) // AuthorizePAT function will valid the secret and check the given scope exists. - AuthorizePAT(ctx context.Context, userID, patID string, entityType EntityType, optionalDomainID string, operation Operation, entityID string) error + AuthorizePAT(ctx context.Context, userID, patID string, entityType EntityType, domainID string, operation Operation, entityID string) error } // PATSRepository specifies PATS persistence API. @@ -456,7 +675,7 @@ type PATSRepository interface { RemoveScope(ctx context.Context, userID string, scopesIDs ...string) error - CheckScope(ctx context.Context, userID, patID string, entityType EntityType, optionalDomainID string, operation Operation, entityID string) error + CheckScope(ctx context.Context, userID, patID string, entityType EntityType, domainID string, operation Operation, entityID string) error RemoveAllScope(ctx context.Context, patID string) error } @@ -464,7 +683,7 @@ type PATSRepository interface { type Cache interface { Save(ctx context.Context, userID string, scopes []Scope) error - CheckScope(ctx context.Context, userID, patID, optionalDomainID string, entityType EntityType, operation Operation, entityID string) bool + CheckScope(ctx context.Context, userID, patID string, entityType EntityType, domainID string, operation Operation, entityID string) bool Remove(ctx context.Context, userID string, scopesID []string) error diff --git a/auth/pat_test.go b/auth/pat_test.go index 315bffd732..65e3dbbd04 100644 --- a/auth/pat_test.go +++ b/auth/pat_test.go @@ -17,54 +17,54 @@ func TestOperationString(t *testing.T) { expected string }{ { - desc: "Create operation", - op: auth.CreateOp, - expected: "create", + desc: "Client create operation", + op: auth.ClientCreateOp, + expected: auth.ClientCreateOp.String(), }, { - desc: "Read operation", - op: auth.ReadOp, - expected: "read", + desc: "Client view operation", + op: auth.ClientViewOp, + expected: auth.ClientViewOp.String(), }, { - desc: "List operation", - op: auth.ListOp, - expected: "list", + desc: "Client list operation", + op: auth.ClientListOp, + expected: auth.ClientListOp.String(), }, { - desc: "Update operation", - op: auth.UpdateOp, - expected: "update", + desc: "Client update operation", + op: auth.ClientUpdateOp, + expected: auth.ClientUpdateOp.String(), }, { - desc: "Delete operation", - op: auth.DeleteOp, - expected: "delete", + desc: "Client delete operation", + op: auth.ClientDeleteOp, + expected: auth.ClientDeleteOp.String(), }, { - desc: "Share operation", - op: auth.ShareOp, - expected: "share", + desc: "Dashboard share operation", + op: auth.DashboardShareOp, + expected: auth.DashboardShareOp.String(), }, { - desc: "Unshare operation", - op: auth.UnshareOp, - expected: "unshare", + desc: "Dashboard unshare operation", + op: auth.DashboardUnshareOp, + expected: auth.DashboardUnshareOp.String(), }, { - desc: "Publish operation", - op: auth.PublishOp, - expected: "publish", + desc: "Message publish operation", + op: auth.MessagePublishOp, + expected: auth.MessagePublishOp.String(), }, { - desc: "Subscribe operation", - op: auth.SubscribeOp, - expected: "subscribe", + desc: "Message subscribe operation", + op: auth.MessageSubscribeOp, + expected: auth.MessageSubscribeOp.String(), }, { desc: "Unknown operation", - op: auth.Operation(100), - expected: "unknown operation type 100", + op: auth.Operation(9999), + expected: "unknown operation type 9999", }, } @@ -84,20 +84,20 @@ func TestOperationValidString(t *testing.T) { err bool }{ { - desc: "Valid create operation", - op: auth.CreateOp, - expected: "create", + desc: "Valid client create operation", + op: auth.ClientCreateOp, + expected: auth.ClientCreateOp.String(), err: false, }, { - desc: "Valid read operation", - op: auth.ReadOp, - expected: "read", + desc: "Valid client view operation", + op: auth.ClientViewOp, + expected: auth.ClientViewOp.String(), err: false, }, { desc: "Invalid operation", - op: auth.Operation(100), + op: auth.Operation(9999), expected: "", err: true, }, @@ -124,57 +124,57 @@ func TestParseOperation(t *testing.T) { err bool }{ { - desc: "Parse create", - op: "create", - expected: auth.CreateOp, + desc: "Parse client_create", + op: auth.ClientCreateOp.String(), + expected: auth.ClientCreateOp, err: false, }, { - desc: "Parse read", - op: "read", - expected: auth.ReadOp, + desc: "Parse client_view", + op: auth.ClientViewOp.String(), + expected: auth.ClientViewOp, err: false, }, { - desc: "Parse list", - op: "list", - expected: auth.ListOp, + desc: "Parse client_list", + op: auth.ClientListOp.String(), + expected: auth.ClientListOp, err: false, }, { - desc: "Parse update", - op: "update", - expected: auth.UpdateOp, + desc: "Parse client_update", + op: auth.ClientUpdateOp.String(), + expected: auth.ClientUpdateOp, err: false, }, { - desc: "Parse delete", - op: "delete", - expected: auth.DeleteOp, + desc: "Parse client_delete", + op: auth.ClientDeleteOp.String(), + expected: auth.ClientDeleteOp, err: false, }, { - desc: "Parse share", - op: "share", - expected: auth.ShareOp, + desc: "Parse dashboard_share", + op: auth.DashboardShareOp.String(), + expected: auth.DashboardShareOp, err: false, }, { - desc: "Parse unshare", - op: "unshare", - expected: auth.UnshareOp, + desc: "Parse dashboard_unshare", + op: auth.DashboardUnshareOp.String(), + expected: auth.DashboardUnshareOp, err: false, }, { - desc: "Parse publish", - op: "publish", - expected: auth.PublishOp, + desc: "Parse message_publish", + op: auth.MessagePublishOp.String(), + expected: auth.MessagePublishOp, err: false, }, { - desc: "Parse subscribe", - op: "subscribe", - expected: auth.SubscribeOp, + desc: "Parse message_subscribe", + op: auth.MessageSubscribeOp.String(), + expected: auth.MessageSubscribeOp, err: false, }, { @@ -206,21 +206,21 @@ func TestOperationMarshalJSON(t *testing.T) { err error }{ { - desc: "Marshal create", - op: auth.CreateOp, - expected: []byte(`"create"`), + desc: "Marshal client_create", + op: auth.ClientCreateOp, + expected: []byte(`"` + auth.ClientCreateOp.String() + `"`), err: nil, }, { - desc: "Marshal read", - op: auth.ReadOp, - expected: []byte(`"read"`), + desc: "Marshal client_view", + op: auth.ClientViewOp, + expected: []byte(`"` + auth.ClientViewOp.String() + `"`), err: nil, }, { - desc: "Marshal delete", - op: auth.DeleteOp, - expected: []byte(`"delete"`), + desc: "Marshal client_delete", + op: auth.ClientDeleteOp, + expected: []byte(`"` + auth.ClientDeleteOp.String() + `"`), err: nil, }, } @@ -242,15 +242,15 @@ func TestOperationUnmarshalJSON(t *testing.T) { err bool }{ { - desc: "Unmarshal create", - data: []byte(`"create"`), - expected: auth.CreateOp, + desc: "Unmarshal client_create", + data: []byte(`"` + auth.ClientCreateOp.String() + `"`), + expected: auth.ClientCreateOp, err: false, }, { - desc: "Unmarshal read", - data: []byte(`"read"`), - expected: auth.ReadOp, + desc: "Unmarshal client_view", + data: []byte(`"` + auth.ClientViewOp.String() + `"`), + expected: auth.ClientViewOp, err: false, }, { @@ -283,15 +283,15 @@ func TestOperationMarshalText(t *testing.T) { err error }{ { - desc: "Marshal create as text", - op: auth.CreateOp, - expected: []byte("create"), + desc: "Marshal client_create as text", + op: auth.ClientCreateOp, + expected: []byte(auth.ClientCreateOp.String()), err: nil, }, { - desc: "Marshal read as text", - op: auth.ReadOp, - expected: []byte("read"), + desc: "Marshal client_view as text", + op: auth.ClientViewOp, + expected: []byte(auth.ClientViewOp.String()), err: nil, }, } @@ -313,15 +313,15 @@ func TestOperationUnmarshalText(t *testing.T) { err bool }{ { - desc: "Unmarshal create from text", - data: []byte("create"), - expected: auth.CreateOp, + desc: "Unmarshal client_create from text", + data: []byte(auth.ClientCreateOp.String()), + expected: auth.ClientCreateOp, err: false, }, { - desc: "Unmarshal read from text", - data: []byte("read"), - expected: auth.ReadOp, + desc: "Unmarshal client_view from text", + data: []byte(auth.ClientViewOp.String()), + expected: auth.ClientViewOp, err: false, }, { @@ -367,16 +367,6 @@ func TestEntityTypeString(t *testing.T) { et: auth.ClientsType, expected: "clients", }, - { - desc: "Domains entity type", - et: auth.DomainsType, - expected: "domains", - }, - { - desc: "Users entity type", - et: auth.UsersType, - expected: "users", - }, { desc: "Dashboard entity type", et: auth.DashboardType, @@ -427,18 +417,6 @@ func TestParseEntityType(t *testing.T) { expected: auth.ClientsType, err: false, }, - { - desc: "Parse domains", - et: "domains", - expected: auth.DomainsType, - err: false, - }, - { - desc: "Parse users", - et: "users", - expected: auth.UsersType, - err: false, - }, { desc: "Parse dashboards", et: "dashboards", diff --git a/auth/postgres/init.go b/auth/postgres/init.go index dfdec3a748..7f19c5a058 100644 --- a/auth/postgres/init.go +++ b/auth/postgres/init.go @@ -125,6 +125,15 @@ func Migration() *migrate.MemoryMigrationSource { `ALTER TABLE pats ALTER COLUMN last_used_at TYPE TIMESTAMP;`, }, }, + { + Id: "auth_7", + Up: []string{ + `ALTER TABLE pat_scopes RENAME COLUMN optional_domain_id TO domain_id;`, + }, + Down: []string{ + `ALTER TABLE pat_scopes RENAME COLUMN domain_id TO optional_domain_id;`, + }, + }, }, } } diff --git a/auth/postgres/pat.go b/auth/postgres/pat.go index 23aa8f9b28..99090a28a7 100644 --- a/auth/postgres/pat.go +++ b/auth/postgres/pat.go @@ -26,12 +26,12 @@ type dbPat struct { } type dbScope struct { - ID string `db:"id,omitempty"` - PatID string `db:"pat_id,omitempty"` - OptionalDomainID string `db:"optional_domain_id,omitempty"` - EntityType string `db:"entity_type,omitempty"` - EntityID string `db:"entity_id,omitempty"` - Operation string `db:"operation,omitempty"` + ID string `db:"id,omitempty"` + PatID string `db:"pat_id,omitempty"` + DomainID string `db:"domain_id,omitempty"` + EntityType string `db:"entity_type,omitempty"` + EntityID string `db:"entity_id,omitempty"` + Operation string `db:"operation,omitempty"` } type dbPagemeta struct { @@ -97,12 +97,12 @@ func toAuthScope(dsc []dbScope) ([]auth.Scope, error) { return []auth.Scope{}, err } scope = append(scope, auth.Scope{ - ID: s.ID, - PatID: s.PatID, - OptionalDomainID: s.OptionalDomainID, - EntityType: entityType, - EntityID: s.EntityID, - Operation: operation, + ID: s.ID, + PatID: s.PatID, + DomainID: s.DomainID, + EntityType: entityType, + EntityID: s.EntityID, + Operation: operation, }) } @@ -152,12 +152,12 @@ func toDBScope(sc []auth.Scope) []dbScope { var scopes []dbScope for _, s := range sc { scopes = append(scopes, dbScope{ - ID: s.ID, - PatID: s.PatID, - OptionalDomainID: s.OptionalDomainID, - EntityType: s.EntityType.String(), - EntityID: s.EntityID, - Operation: s.Operation.String(), + ID: s.ID, + PatID: s.PatID, + DomainID: s.DomainID, + EntityType: s.EntityType.String(), + EntityID: s.EntityID, + Operation: s.Operation.String(), }) } return scopes diff --git a/auth/postgres/repo.go b/auth/postgres/repo.go index dccf7c9a1e..b4602f5a55 100644 --- a/auth/postgres/repo.go +++ b/auth/postgres/repo.go @@ -374,8 +374,8 @@ func (pr *patRepo) RemoveAllPAT(ctx context.Context, userID string) error { func (pr *patRepo) AddScope(ctx context.Context, userID string, scopes []auth.Scope) error { q := ` - INSERT INTO pat_scopes (id, pat_id, entity_type, optional_domain_id, operation, entity_id) - VALUES (:id, :pat_id, :entity_type, :optional_domain_id, :operation, :entity_id)` + INSERT INTO pat_scopes (id, pat_id, entity_type, domain_id, operation, entity_id) + VALUES (:id, :pat_id, :entity_type, :domain_id, :operation, :entity_id)` var newScopes []auth.Scope @@ -410,17 +410,17 @@ func (pr *patRepo) processScope(ctx context.Context, sc auth.Scope) (auth.Scope, FROM pat_scopes WHERE pat_id = :pat_id AND entity_type = :entity_type - AND optional_domain_id = :optional_domain_id + AND domain_id = :domain_id AND operation = :operation AND entity_id = :entity_id LIMIT 1` params := dbScope{ - PatID: sc.PatID, - OptionalDomainID: sc.OptionalDomainID, - EntityType: sc.EntityType.String(), - Operation: sc.Operation.String(), - EntityID: auth.AnyIDs, + PatID: sc.PatID, + DomainID: sc.DomainID, + EntityType: sc.EntityType.String(), + Operation: sc.Operation.String(), + EntityID: auth.AnyIDs, } rows, err := pr.db.NamedQueryContext(ctx, q, params) @@ -442,10 +442,10 @@ func (pr *patRepo) processScope(ctx context.Context, sc auth.Scope) (auth.Scope, if sc.EntityID == auth.AnyIDs { newParams := dbScope{ - PatID: sc.PatID, - OptionalDomainID: sc.OptionalDomainID, - EntityType: sc.EntityType.String(), - Operation: sc.Operation.String(), + PatID: sc.PatID, + DomainID: sc.DomainID, + EntityType: sc.EntityType.String(), + Operation: sc.Operation.String(), } checkEntityQuery := ` @@ -453,7 +453,7 @@ func (pr *patRepo) processScope(ctx context.Context, sc auth.Scope) (auth.Scope, FROM pat_scopes WHERE pat_id = :pat_id AND entity_type = :entity_type - AND optional_domain_id = :optional_domain_id + AND domain_id = :domain_id AND operation = :operation LIMIT 1` @@ -476,7 +476,7 @@ func (pr *patRepo) processScope(ctx context.Context, sc auth.Scope) (auth.Scope, SET entity_id = :entity_id WHERE pat_id = :pat_id AND entity_type = :entity_type - AND optional_domain_id = :optional_domain_id + AND domain_id = :domain_id AND operation = :operation` rows, err = pr.db.NamedQueryContext(ctx, updateWithWildcardQuery, params) @@ -511,28 +511,28 @@ func (pr *patRepo) RemoveScope(ctx context.Context, userID string, scopesIDs ... return nil } -func (pr *patRepo) CheckScope(ctx context.Context, userID, patID string, entityType auth.EntityType, optionalDomainID string, operation auth.Operation, entityID string) error { +func (pr *patRepo) CheckScope(ctx context.Context, userID, patID string, entityType auth.EntityType, domainID string, operation auth.Operation, entityID string) error { q := ` - SELECT id, pat_id, entity_type, optional_domain_id, operation, entity_id + SELECT id, pat_id, entity_type, domain_id, operation, entity_id FROM pat_scopes WHERE pat_id = :pat_id AND entity_type = :entity_type - AND optional_domain_id = :optional_domain_id + AND domain_id = :domain_id AND operation = :operation AND (entity_id = :entity_id OR entity_id = '*') LIMIT 1` - authorized := pr.cache.CheckScope(ctx, userID, patID, optionalDomainID, entityType, operation, entityID) + authorized := pr.cache.CheckScope(ctx, userID, patID, entityType, domainID, operation, entityID) if authorized { return nil } scope := dbScope{ - PatID: patID, - EntityType: entityType.String(), - OptionalDomainID: optionalDomainID, - Operation: operation.String(), - EntityID: entityID, + PatID: patID, + EntityType: entityType.String(), + DomainID: domainID, + Operation: operation.String(), + EntityID: entityID, } rows, err := pr.db.NamedQueryContext(ctx, q, scope) @@ -556,19 +556,19 @@ func (pr *patRepo) CheckScope(ctx context.Context, userID, patID string, entityT return errors.Wrap(repoerr.ErrViewEntity, err) } authScope := auth.Scope{ - ID: sc.ID, - PatID: sc.PatID, - OptionalDomainID: sc.OptionalDomainID, - EntityType: entityType, - EntityID: sc.EntityID, - Operation: operation, + ID: sc.ID, + PatID: sc.PatID, + DomainID: sc.DomainID, + EntityType: entityType, + EntityID: sc.EntityID, + Operation: operation, } if err := pr.cache.Save(ctx, userID, []auth.Scope{authScope}); err != nil { return err } - if authScope.Authorized(entityType, optionalDomainID, operation, entityID) { + if authScope.Authorized(entityType, domainID, operation, entityID) { return nil } } @@ -625,7 +625,7 @@ func (pr *patRepo) RetrieveScope(ctx context.Context, pm auth.ScopesPageMeta) (a func (pr *patRepo) retrieveScopeFromDB(ctx context.Context, pm dbPagemeta) ([]auth.Scope, error) { q := ` - SELECT id, pat_id, entity_type, optional_domain_id, operation, entity_id + SELECT id, pat_id, entity_type, domain_id, operation, entity_id FROM pat_scopes WHERE pat_id = :pat_id OFFSET :offset LIMIT :limit` scopeRows, err := pr.db.NamedQueryContext(ctx, q, pm) if err != nil { diff --git a/auth/scope_test.go b/auth/scope_test.go index 58b7579454..7e54ba00d0 100644 --- a/auth/scope_test.go +++ b/auth/scope_test.go @@ -14,126 +14,126 @@ import ( func TestScopeAuthorized(t *testing.T) { cases := []struct { - desc string - scope *auth.Scope - entityType auth.EntityType - optionalDomainID string - operation auth.Operation - entityID string - expected bool + desc string + scope *auth.Scope + entityType auth.EntityType + domainID string + operation auth.Operation + entityID string + expected bool }{ { desc: "Authorized with matching entity type, domain, operation and entity ID", scope: &auth.Scope{ - EntityType: auth.GroupsType, - OptionalDomainID: "domain1", - Operation: auth.CreateOp, - EntityID: "entity1", + EntityType: auth.GroupsType, + DomainID: "domain1", + Operation: auth.GroupCreateOp, + EntityID: "entity1", }, - entityType: auth.GroupsType, - optionalDomainID: "domain1", - operation: auth.CreateOp, - entityID: "entity1", - expected: true, + entityType: auth.GroupsType, + domainID: "domain1", + operation: auth.GroupCreateOp, + entityID: "entity1", + expected: true, }, { desc: "Authorized with wildcard entity ID", scope: &auth.Scope{ - EntityType: auth.GroupsType, - OptionalDomainID: "domain1", - Operation: auth.CreateOp, - EntityID: "*", + EntityType: auth.GroupsType, + DomainID: "domain1", + Operation: auth.GroupCreateOp, + EntityID: "*", }, - entityType: auth.GroupsType, - optionalDomainID: "domain1", - operation: auth.CreateOp, - entityID: "any-entity", - expected: true, + entityType: auth.GroupsType, + domainID: "domain1", + operation: auth.GroupCreateOp, + entityID: "any-entity", + expected: true, }, { desc: "Authorized without domain ID", scope: &auth.Scope{ - EntityType: auth.UsersType, - OptionalDomainID: "", - Operation: auth.ReadOp, - EntityID: "user1", + EntityType: auth.ClientsType, + DomainID: "", + Operation: auth.ClientViewOp, + EntityID: "client1", }, - entityType: auth.UsersType, - optionalDomainID: "", - operation: auth.ReadOp, - entityID: "user1", - expected: true, + entityType: auth.ClientsType, + domainID: "domain1", + operation: auth.ClientViewOp, + entityID: "client1", + expected: true, }, { desc: "Not authorized with different entity type", scope: &auth.Scope{ - EntityType: auth.GroupsType, - OptionalDomainID: "domain1", - Operation: auth.CreateOp, - EntityID: "entity1", + EntityType: auth.GroupsType, + DomainID: "domain1", + Operation: auth.GroupCreateOp, + EntityID: "entity1", }, - entityType: auth.ChannelsType, - optionalDomainID: "domain1", - operation: auth.CreateOp, - entityID: "entity1", - expected: false, + entityType: auth.ChannelsType, + domainID: "domain1", + operation: auth.GroupCreateOp, + entityID: "entity1", + expected: false, }, { desc: "Not authorized with different domain ID", scope: &auth.Scope{ - EntityType: auth.GroupsType, - OptionalDomainID: "domain1", - Operation: auth.CreateOp, - EntityID: "entity1", + EntityType: auth.GroupsType, + DomainID: "domain1", + Operation: auth.GroupCreateOp, + EntityID: "entity1", }, - entityType: auth.GroupsType, - optionalDomainID: "domain2", - operation: auth.CreateOp, - entityID: "entity1", - expected: false, + entityType: auth.GroupsType, + domainID: "domain2", + operation: auth.GroupCreateOp, + entityID: "entity1", + expected: false, }, { desc: "Not authorized with different operation", scope: &auth.Scope{ - EntityType: auth.GroupsType, - OptionalDomainID: "domain1", - Operation: auth.CreateOp, - EntityID: "entity1", + EntityType: auth.GroupsType, + DomainID: "domain1", + Operation: auth.GroupCreateOp, + EntityID: "entity1", }, - entityType: auth.GroupsType, - optionalDomainID: "domain1", - operation: auth.DeleteOp, - entityID: "entity1", - expected: false, + entityType: auth.GroupsType, + domainID: "domain1", + operation: auth.GroupDeleteOp, + entityID: "entity1", + expected: false, }, { desc: "Not authorized with different entity ID", scope: &auth.Scope{ - EntityType: auth.GroupsType, - OptionalDomainID: "domain1", - Operation: auth.CreateOp, - EntityID: "entity1", + EntityType: auth.GroupsType, + DomainID: "domain1", + Operation: auth.GroupCreateOp, + EntityID: "entity1", }, - entityType: auth.GroupsType, - optionalDomainID: "domain1", - operation: auth.CreateOp, - entityID: "entity2", - expected: false, + entityType: auth.GroupsType, + domainID: "domain1", + operation: auth.GroupCreateOp, + entityID: "entity2", + expected: false, }, { - desc: "Not authorized with nil scope", - scope: nil, - entityType: auth.GroupsType, - optionalDomainID: "domain1", - operation: auth.CreateOp, - entityID: "entity1", - expected: false, + desc: "Not authorized with nil scope", + scope: nil, + entityType: auth.GroupsType, + domainID: "domain1", + operation: auth.GroupCreateOp, + entityID: "entity1", + expected: false, }, } for _, tc := range cases { t.Run(tc.desc, func(t *testing.T) { - result := tc.scope.Authorized(tc.entityType, tc.optionalDomainID, tc.operation, tc.entityID) + result := tc.scope.Authorized(tc.entityType, tc.domainID, tc.operation, tc.entityID) assert.Equal(t, tc.expected, result, "Authorized() = %v, expected %v", result, tc.expected) }) } @@ -148,60 +148,60 @@ func TestScopeValidate(t *testing.T) { { desc: "Valid scope for groups with domain ID", scope: &auth.Scope{ - EntityType: auth.GroupsType, - OptionalDomainID: "domain1", - Operation: auth.CreateOp, - EntityID: "entity1", + EntityType: auth.GroupsType, + DomainID: "domain1", + Operation: auth.GroupCreateOp, + EntityID: "entity1", }, err: nil, }, { desc: "Valid scope for channels with domain ID", scope: &auth.Scope{ - EntityType: auth.ChannelsType, - OptionalDomainID: "domain1", - Operation: auth.ReadOp, - EntityID: "channel1", + EntityType: auth.ChannelsType, + DomainID: "domain1", + Operation: auth.ChannelViewOp, + EntityID: "channel1", }, err: nil, }, { desc: "Valid scope for clients with domain ID", scope: &auth.Scope{ - EntityType: auth.ClientsType, - OptionalDomainID: "domain1", - Operation: auth.UpdateOp, - EntityID: "client1", + EntityType: auth.ClientsType, + DomainID: "domain1", + Operation: auth.ClientUpdateOp, + EntityID: "client1", }, err: nil, }, { - desc: "Valid scope for users without domain ID", + desc: "Valid scope for messages with domain ID", scope: &auth.Scope{ - EntityType: auth.UsersType, - OptionalDomainID: "", - Operation: auth.DeleteOp, - EntityID: "user1", + EntityType: auth.MessagesType, + DomainID: "domain1", + Operation: auth.MessagePublishOp, + EntityID: "message1", }, err: nil, }, { - desc: "Valid scope for domains without domain ID", + desc: "Valid scope for dashboard with domain ID", scope: &auth.Scope{ - EntityType: auth.DomainsType, - OptionalDomainID: "", - Operation: auth.ListOp, - EntityID: "domain1", + EntityType: auth.DashboardType, + DomainID: "domain1", + Operation: auth.DashboardShareOp, + EntityID: "dashboard1", }, err: nil, }, { desc: "Valid scope with wildcard entity ID", scope: &auth.Scope{ - EntityType: auth.GroupsType, - OptionalDomainID: "domain1", - Operation: auth.CreateOp, - EntityID: "*", + EntityType: auth.GroupsType, + DomainID: "domain1", + Operation: auth.GroupCreateOp, + EntityID: "*", }, err: nil, }, @@ -213,40 +213,60 @@ func TestScopeValidate(t *testing.T) { { desc: "Invalid scope without entity ID", scope: &auth.Scope{ - EntityType: auth.GroupsType, - OptionalDomainID: "domain1", - Operation: auth.CreateOp, - EntityID: "", + EntityType: auth.GroupsType, + DomainID: "domain1", + Operation: auth.GroupCreateOp, + EntityID: "", }, err: apiutil.ErrMissingEntityID, }, { desc: "Invalid scope for groups without domain ID", scope: &auth.Scope{ - EntityType: auth.GroupsType, - OptionalDomainID: "", - Operation: auth.CreateOp, - EntityID: "entity1", + EntityType: auth.GroupsType, + DomainID: "", + Operation: auth.GroupCreateOp, + EntityID: "entity1", }, err: apiutil.ErrMissingDomainID, }, { desc: "Invalid scope for channels without domain ID", scope: &auth.Scope{ - EntityType: auth.ChannelsType, - OptionalDomainID: "", - Operation: auth.CreateOp, - EntityID: "channel1", + EntityType: auth.ChannelsType, + DomainID: "", + Operation: auth.ChannelCreateOp, + EntityID: "channel1", }, err: apiutil.ErrMissingDomainID, }, { desc: "Invalid scope for clients without domain ID", scope: &auth.Scope{ - EntityType: auth.ClientsType, - OptionalDomainID: "", - Operation: auth.CreateOp, - EntityID: "client1", + EntityType: auth.ClientsType, + DomainID: "", + Operation: auth.ClientCreateOp, + EntityID: "client1", + }, + err: apiutil.ErrMissingDomainID, + }, + { + desc: "Invalid scope for dashboard without domain ID", + scope: &auth.Scope{ + EntityType: auth.DashboardType, + DomainID: "", + Operation: auth.DashboardShareOp, + EntityID: "dashboard1", + }, + err: apiutil.ErrMissingDomainID, + }, + { + desc: "Invalid scope for messages without domain ID", + scope: &auth.Scope{ + EntityType: auth.MessagesType, + DomainID: "", + Operation: auth.MessagePublishOp, + EntityID: "message1", }, err: apiutil.ErrMissingDomainID, }, diff --git a/auth/service.go b/auth/service.go index b88a8efaaa..f1a858df6c 100644 --- a/auth/service.go +++ b/auth/service.go @@ -212,7 +212,7 @@ func (svc service) RetrieveJWKS() []PublicKeyInfo { func (svc service) Authorize(ctx context.Context, pr policies.Policy) error { if pr.PatID != "" && pr.TokenType == PersonalAccessTokenType { - if err := svc.AuthorizePAT(ctx, pr.UserID, pr.PatID, EntityType(pr.EntityType), pr.OptionalDomainID, Operation(pr.Operation), pr.EntityID); err != nil { + if err := svc.AuthorizePAT(ctx, pr.UserID, pr.PatID, EntityType(pr.EntityType), pr.DomainID, Operation(pr.Operation), pr.EntityID); err != nil { return err } return nil @@ -735,8 +735,8 @@ func (svc service) IdentifyPAT(ctx context.Context, secret string) (PAT, error) return pat, nil } -func (svc service) AuthorizePAT(ctx context.Context, userID, patID string, entityType EntityType, optionalDomainID string, operation Operation, entityID string) error { - if err := svc.pats.CheckScope(ctx, userID, patID, entityType, optionalDomainID, operation, entityID); err != nil { +func (svc service) AuthorizePAT(ctx context.Context, userID, patID string, entityType EntityType, domainID string, operation Operation, entityID string) error { + if err := svc.pats.CheckScope(ctx, userID, patID, entityType, domainID, operation, entityID); err != nil { return errors.Wrap(svcerr.ErrAuthorization, err) } diff --git a/channels/middleware/authorization.go b/channels/middleware/authorization.go index f3a3ca9010..046793330a 100644 --- a/channels/middleware/authorization.go +++ b/channels/middleware/authorization.go @@ -49,6 +49,7 @@ type authorizationMiddleware struct { repo channels.Repository authz smqauthz.Authorization entitiesOps permissions.EntitiesOperations[permissions.Operation] + authOps map[string]auth.Operation rolemgr.RoleManagerAuthorizationMiddleware } @@ -59,12 +60,13 @@ func NewAuthorization( authz smqauthz.Authorization, repo channels.Repository, entitiesOps permissions.EntitiesOperations[permissions.Operation], + authOps map[string]auth.Operation, roleOps permissions.Operations[permissions.RoleOperation], ) (channels.Service, error) { if err := entitiesOps.Validate(); err != nil { return nil, err } - ram, err := rolemgr.NewAuthorization(policies.ChannelType, svc, authz, roleOps) + ram, err := rolemgr.NewAuthorization(policies.ChannelType, svc, authz, roleOps, authOps) if err != nil { return nil, err } @@ -74,25 +76,19 @@ func NewAuthorization( authz: authz, repo: repo, entitiesOps: entitiesOps, + authOps: authOps, RoleManagerAuthorizationMiddleware: ram, }, nil } func (am *authorizationMiddleware) CreateChannels(ctx context.Context, session authn.Session, chs ...channels.Channel) ([]channels.Channel, []roles.RoleProvision, error) { - req := smqauthz.PolicyReq{ - Domain: session.DomainID, - SubjectType: policies.UserType, - Subject: session.DomainUserID, - ObjectType: policies.DomainType, - Object: session.DomainID, - UserID: session.UserID, - PatID: session.PatID, - EntityType: auth.ChannelsType, - OptionalDomainID: session.DomainID, - Operation: auth.CreateOp, - EntityID: auth.AnyIDs, - } - if err := am.authorize(ctx, session, policies.DomainType, domains.OpCreateDomainChannels, req); err != nil { + if err := am.authorize(ctx, session, policies.DomainType, domains.OpCreateDomainChannels, smqauthz.PolicyReq{ + Domain: session.DomainID, + SubjectType: policies.UserType, + Subject: session.DomainUserID, + ObjectType: policies.DomainType, + Object: session.DomainID, + }); err != nil { return []channels.Channel{}, []roles.RoleProvision{}, errors.Wrap(err, errDomainCreateChannels) } @@ -115,17 +111,11 @@ func (am *authorizationMiddleware) CreateChannels(ctx context.Context, session a func (am *authorizationMiddleware) ViewChannel(ctx context.Context, session authn.Session, id string, withRoles bool) (channels.Channel, error) { if err := am.authorize(ctx, session, policies.ChannelType, channels.OpViewChannel, smqauthz.PolicyReq{ - Domain: session.DomainID, - SubjectType: policies.UserType, - Subject: session.DomainUserID, - ObjectType: policies.ChannelType, - Object: id, - UserID: session.UserID, - PatID: session.PatID, - EntityType: auth.ChannelsType, - OptionalDomainID: session.DomainID, - Operation: auth.ReadOp, - EntityID: id, + Domain: session.DomainID, + SubjectType: policies.UserType, + Subject: session.DomainUserID, + ObjectType: policies.ChannelType, + Object: id, }); err != nil { return channels.Channel{}, errors.Wrap(err, errView) } @@ -134,7 +124,13 @@ func (am *authorizationMiddleware) ViewChannel(ctx context.Context, session auth } func (am *authorizationMiddleware) ListChannels(ctx context.Context, session authn.Session, pm channels.Page) (channels.ChannelsPage, error) { - if err := am.checkSuperAdmin(ctx, session); err == nil { + if err := am.authorize(ctx, session, policies.DomainType, domains.OpListDomainChannels, smqauthz.PolicyReq{ + Domain: session.DomainID, + SubjectType: policies.UserType, + Subject: session.DomainUserID, + ObjectType: policies.DomainType, + Object: session.DomainID, + }); err == nil { session.SuperAdmin = true } @@ -335,7 +331,7 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, session authn. req.TokenType = session.Type req.UserID = session.UserID req.PatID = session.PatID - req.OptionalDomainID = session.DomainID + req.DomainID = session.DomainID perm, err := am.entitiesOps.GetPermission(entityType, op) if err != nil { @@ -348,33 +344,12 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, session authn. req.EntityType = auth.ChannelsType req.EntityID = req.Object - switch op { - case channels.OpViewChannel: - req.Operation = auth.ReadOp - case channels.OpListUserChannels: - req.Operation = auth.ListOp - req.EntityID = auth.AnyIDs - case channels.OpUpdateChannel, - channels.OpUpdateChannelTags, - channels.OpEnableChannel, - channels.OpDisableChannel, - channels.OpSetParentGroup: - req.Operation = auth.UpdateOp - case channels.OpDeleteChannel, - channels.OpRemoveParentGroup: - req.Operation = auth.DeleteOp - case channels.OpConnectClient: - req.Operation = auth.CreateOp - case channels.OpDisconnectClient: - req.Operation = auth.DeleteOp - case domains.OpCreateDomainChannels, - domains.OpListDomainChannels: - if op == domains.OpCreateDomainChannels { - req.Operation = auth.CreateOp - } else { - req.Operation = auth.ListOp + opName := am.entitiesOps.OperationName(entityType, op) + if authOp, ok := am.authOps[opName]; ok { + req.Operation = authOp + if op == channels.OpListUserChannels || op == domains.OpCreateDomainChannels || op == domains.OpListDomainChannels { + req.EntityID = auth.AnyIDs } - req.EntityID = auth.AnyIDs } } diff --git a/clients/middleware/authorization.go b/clients/middleware/authorization.go index 50b05f8ffd..10c5366b5e 100644 --- a/clients/middleware/authorization.go +++ b/clients/middleware/authorization.go @@ -42,6 +42,7 @@ type authorizationMiddleware struct { repo clients.Repository authz smqauthz.Authorization entitiesOps permissions.EntitiesOperations[permissions.Operation] + authOps map[string]auth.Operation rolemgr.RoleManagerAuthorizationMiddleware } @@ -52,12 +53,13 @@ func NewAuthorization( authz smqauthz.Authorization, repo clients.Repository, entitiesOps permissions.EntitiesOperations[permissions.Operation], + authOps map[string]auth.Operation, roleOps permissions.Operations[permissions.RoleOperation], ) (clients.Service, error) { if err := entitiesOps.Validate(); err != nil { return nil, err } - ram, err := rolemgr.NewAuthorization(policies.ClientType, svc, authz, roleOps) + ram, err := rolemgr.NewAuthorization(policies.ClientType, svc, authz, roleOps, authOps) if err != nil { return nil, err } @@ -67,6 +69,7 @@ func NewAuthorization( authz: authz, repo: repo, entitiesOps: entitiesOps, + authOps: authOps, RoleManagerAuthorizationMiddleware: ram, }, nil } @@ -259,7 +262,7 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, session authn. req.TokenType = session.Type req.UserID = session.UserID req.PatID = session.PatID - req.OptionalDomainID = session.DomainID + req.DomainID = session.DomainID perm, err := am.entitiesOps.GetPermission(entityType, op) if err != nil { @@ -270,44 +273,13 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, session authn. if req.PatID != "" && req.TokenType == authn.PersonalAccessToken { req.EntityID = req.Object + req.EntityType = auth.ClientsType - switch entityType { - case policies.ClientType: - req.EntityType = auth.ClientsType - switch op { - case clients.OpViewClient: - req.Operation = auth.ReadOp - case clients.OpListUserClients: - req.Operation = auth.ListOp + opName := am.entitiesOps.OperationName(entityType, op) + if authOp, ok := am.authOps[opName]; ok { + req.Operation = authOp + if op == clients.OpListUserClients || op == domains.OpCreateDomainClients || op == domains.OpListDomainClients { req.EntityID = auth.AnyIDs - case clients.OpUpdateClient, - clients.OpUpdateClientTags, - clients.OpUpdateClientSecret, - clients.OpEnableClient, - clients.OpDisableClient, - clients.OpSetParentGroup: - req.Operation = auth.UpdateOp - case clients.OpDeleteClient, - clients.OpRemoveParentGroup: - req.Operation = auth.DeleteOp - } - case policies.DomainType: - req.EntityType = auth.ClientsType - switch op { - case domains.OpCreateDomainClients: - req.Operation = auth.CreateOp - req.EntityID = auth.AnyIDs - case domains.OpListDomainClients: - req.Operation = auth.ListOp - req.EntityID = auth.AnyIDs - } - case policies.GroupType: - req.EntityType = auth.ClientsType - switch op { - case groups.OpGroupSetChildClient: - req.Operation = auth.UpdateOp - case groups.OpGroupRemoveChildClient: - req.Operation = auth.DeleteOp } } } diff --git a/cmd/channels/main.go b/cmd/channels/main.go index 433a06bdbc..03e69f37bd 100644 --- a/cmd/channels/main.go +++ b/cmd/channels/main.go @@ -443,7 +443,33 @@ func newService(ctx context.Context, db *sqlx.DB, dbConfig pgclient.Config, cach return nil, nil, fmt.Errorf("failed to create role operations: %w", err) } - svc, err = middleware.NewAuthorization(policies.ChannelType, svc, authz, repo, entitiesOps, roleOps) + channelAuthOps, channelRoleAuthOps, err := permConfig.GetAuthOperations("channels") + if err != nil { + return nil, nil, fmt.Errorf("failed to get channel auth operations: %w", err) + } + domainAuthOps, _, err := permConfig.GetAuthOperations("domains") + if err != nil { + return nil, nil, fmt.Errorf("failed to get domain auth operations: %w", err) + } + + authOps := make(map[string]auth.Operation) + for opName, authOpStr := range channelAuthOps { + if authOp, err := auth.ParseOperation(authOpStr); err == nil { + authOps[opName] = authOp + } + } + for opName, authOpStr := range channelRoleAuthOps { + if authOp, err := auth.ParseOperation(authOpStr); err == nil { + authOps[opName] = authOp + } + } + for opName, authOpStr := range domainAuthOps { + if authOp, err := auth.ParseOperation(authOpStr); err == nil { + authOps[opName] = authOp + } + } + + svc, err = middleware.NewAuthorization(policies.ChannelType, svc, authz, repo, entitiesOps, authOps, roleOps) if err != nil { return nil, nil, err } diff --git a/cmd/clients/main.go b/cmd/clients/main.go index cafddb968d..25eb3d3ffc 100644 --- a/cmd/clients/main.go +++ b/cmd/clients/main.go @@ -436,7 +436,42 @@ func newService(ctx context.Context, db *sqlx.DB, dbConfig pgclient.Config, auth return nil, nil, fmt.Errorf("failed to create role operations: %w", err) } - csvc, err = middleware.NewAuthorization(policies.ClientType, csvc, authz, repo, entitiesOps, roleOps) + clientAuthOps, clientRoleAuthOps, err := permConfig.GetAuthOperations("clients") + if err != nil { + return nil, nil, fmt.Errorf("failed to get client auth operations: %w", err) + } + domainAuthOps, _, err := permConfig.GetAuthOperations("domains") + if err != nil { + return nil, nil, fmt.Errorf("failed to get domain auth operations: %w", err) + } + groupAuthOps, _, err := permConfig.GetAuthOperations("groups") + if err != nil { + return nil, nil, fmt.Errorf("failed to get group auth operations: %w", err) + } + + authOps := make(map[string]auth.Operation) + for opName, authOpStr := range clientAuthOps { + if authOp, err := auth.ParseOperation(authOpStr); err == nil { + authOps[opName] = authOp + } + } + for opName, authOpStr := range clientRoleAuthOps { + if authOp, err := auth.ParseOperation(authOpStr); err == nil { + authOps[opName] = authOp + } + } + for opName, authOpStr := range domainAuthOps { + if authOp, err := auth.ParseOperation(authOpStr); err == nil { + authOps[opName] = authOp + } + } + for opName, authOpStr := range groupAuthOps { + if authOp, err := auth.ParseOperation(authOpStr); err == nil { + authOps[opName] = authOp + } + } + + csvc, err = middleware.NewAuthorization(policies.ClientType, csvc, authz, repo, entitiesOps, authOps, roleOps) if err != nil { return nil, nil, err } diff --git a/cmd/domains/main.go b/cmd/domains/main.go index 0961bab01f..7e98dbc269 100644 --- a/cmd/domains/main.go +++ b/cmd/domains/main.go @@ -317,6 +317,18 @@ func newDomainService(ctx context.Context, domainsRepo domainsSvc.Repository, ca return nil, fmt.Errorf("failed to get domain permissions: %w", err) } + _, domainRoleAuthOps, err := permConfig.GetAuthOperations("domains") + if err != nil { + return nil, fmt.Errorf("failed to get domain auth operations: %w", err) + } + + authOps := make(map[string]auth.Operation) + for opName, authOpStr := range domainRoleAuthOps { + if authOp, err := auth.ParseOperation(authOpStr); err == nil { + authOps[opName] = authOp + } + } + entitiesOps, err := permissions.NewEntitiesOperations( permissions.EntitiesPermission{policies.DomainType: domainOps}, permissions.EntitiesOperationDetails[permissions.Operation]{policies.DomainType: domains.OperationDetails()}, @@ -330,7 +342,7 @@ func newDomainService(ctx context.Context, domainsRepo domainsSvc.Repository, ca return nil, fmt.Errorf("failed to create role operations: %w", err) } - svc, err = dmw.NewAuthorization(policies.DomainType, svc, authz, entitiesOps, roleOps) + svc, err = dmw.NewAuthorization(policies.DomainType, svc, authz, entitiesOps, authOps, roleOps) if err != nil { return nil, err } diff --git a/cmd/groups/main.go b/cmd/groups/main.go index 2a465bd1c5..ece6704310 100644 --- a/cmd/groups/main.go +++ b/cmd/groups/main.go @@ -372,6 +372,33 @@ func newService(ctx context.Context, authz smqauthz.Authorization, policy polici return nil, nil, fmt.Errorf("failed to get domain permissions: %w", err) } + groupAuthOps, groupRoleAuthOps, err := permConfig.GetAuthOperations("groups") + if err != nil { + return nil, nil, fmt.Errorf("failed to get group auth operations: %w", err) + } + + domainAuthOps, _, err := permConfig.GetAuthOperations("domains") + if err != nil { + return nil, nil, fmt.Errorf("failed to get domain auth operations: %w", err) + } + + authOps := make(map[string]auth.Operation) + for opName, authOpStr := range groupAuthOps { + if authOp, err := auth.ParseOperation(authOpStr); err == nil { + authOps[opName] = authOp + } + } + for opName, authOpStr := range groupRoleAuthOps { + if authOp, err := auth.ParseOperation(authOpStr); err == nil { + authOps[opName] = authOp + } + } + for opName, authOpStr := range domainAuthOps { + if authOp, err := auth.ParseOperation(authOpStr); err == nil { + authOps[opName] = authOp + } + } + entitiesOps, err := permissions.NewEntitiesOperations( permissions.EntitiesPermission{ policies.GroupType: groupOps, @@ -391,7 +418,7 @@ func newService(ctx context.Context, authz smqauthz.Authorization, policy polici return nil, nil, fmt.Errorf("failed to create role operations: %w", err) } - svc, err = middleware.NewAuthorization(policies.GroupType, svc, authz, repo, entitiesOps, roleOps) + svc, err = middleware.NewAuthorization(policies.GroupType, svc, authz, repo, entitiesOps, authOps, roleOps) if err != nil { return nil, nil, err } diff --git a/docker/permission.yaml b/docker/permission.yaml index 8201e72dd9..d649b9d723 100644 --- a/docker/permission.yaml +++ b/docker/permission.yaml @@ -3,98 +3,264 @@ clients: operations: - - view: read_permission - - update: update_permission - - update_tags: update_permission - - update_secret: update_permission - - enable: update_permission - - disable: update_permission - - delete: delete_permission - - set_parent_group: set_parent_group_permission - - remove_parent_group: set_parent_group_permission - - connect_to_channel: connect_to_channel_permission - - disconnect_from_channel: connect_to_channel_permission + - view: + permission: read_permission + auth_operation: client_view + - update: + permission: update_permission + auth_operation: client_update + - update_tags: + permission: update_permission + auth_operation: client_update_tags + - update_secret: + permission: update_permission + auth_operation: client_update_secret + - enable: + permission: update_permission + auth_operation: client_enable + - disable: + permission: update_permission + auth_operation: client_disable + - delete: + permission: delete_permission + auth_operation: client_delete + - set_parent_group: + permission: set_parent_group_permission + auth_operation: client_set_parent_group + - remove_parent_group: + permission: set_parent_group_permission + auth_operation: client_remove_parent_group + - connect_to_channel: + permission: connect_to_channel_permission + auth_operation: client_connect_to_channel + - disconnect_from_channel: + permission: connect_to_channel_permission + auth_operation: client_disconnect_from_channel roles_operations: - - add: manage_role_permission - - remove: manage_role_permission - - update: manage_role_permission - - retrieve: view_role_users_permission - - retrieve_all: view_role_users_permission - - add_actions: manage_role_permission - - list_actions: view_role_users_permission - - check_actions_exists: view_role_users_permission - - remove_actions: manage_role_permission - - remove_all_actions: manage_role_permission - - add_members: add_role_users_permission - - list_members: view_role_users_permission - - check_members_exists: view_role_users_permission - - remove_members: remove_role_users_permission - - remove_all_members: remove_role_users_permission + - add: + permission: manage_role_permission + auth_operation: role_add + - remove: + permission: manage_role_permission + auth_operation: role_remove + - update: + permission: manage_role_permission + auth_operation: role_update + - retrieve: + permission: view_role_users_permission + auth_operation: role_retrieve + - retrieve_all: + permission: view_role_users_permission + auth_operation: role_retrieve_all + - add_actions: + permission: manage_role_permission + auth_operation: role_add_actions + - list_actions: + permission: view_role_users_permission + auth_operation: role_list_actions + - check_actions_exists: + permission: view_role_users_permission + auth_operation: role_check_actions_exists + - remove_actions: + permission: manage_role_permission + auth_operation: role_remove_actions + - remove_all_actions: + permission: manage_role_permission + auth_operation: role_remove_all_actions + - add_members: + permission: add_role_users_permission + auth_operation: role_add_members + - list_members: + permission: view_role_users_permission + auth_operation: role_list_members + - check_members_exists: + permission: view_role_users_permission + auth_operation: role_check_members_exists + - remove_members: + permission: remove_role_users_permission + auth_operation: role_remove_members + - remove_all_members: + permission: remove_role_users_permission + auth_operation: role_remove_all_members channels: operations: - - view: read_permission - - update: update_permission - - update_tags: update_permission - - enable: update_permission - - disable: update_permission - - delete: delete_permission - - set_parent_group: set_parent_group_permission - - remove_parent_group: set_parent_group_permission - - connect_client: connect_to_client_permission - - disconnect_client: connect_to_client_permission + - view: + permission: read_permission + auth_operation: channel_view + - update: + permission: update_permission + auth_operation: channel_update + - update_tags: + permission: update_permission + auth_operation: channel_update_tags + - enable: + permission: update_permission + auth_operation: channel_enable + - disable: + permission: update_permission + auth_operation: channel_disable + - delete: + permission: delete_permission + auth_operation: channel_delete + - set_parent_group: + permission: set_parent_group_permission + auth_operation: channel_set_parent_group + - remove_parent_group: + permission: set_parent_group_permission + auth_operation: channel_remove_parent_group + - connect_client: + permission: connect_to_client_permission + auth_operation: channel_connect_to_client + - disconnect_client: + permission: connect_to_client_permission + auth_operation: channel_disconnect_from_client roles_operations: - - add: manage_role_permission - - remove: manage_role_permission - - update: manage_role_permission - - retrieve: view_role_users_permission - - retrieve_all: view_role_users_permission - - add_actions: manage_role_permission - - list_actions: view_role_users_permission - - check_actions_exists: view_role_users_permission - - remove_actions: manage_role_permission - - remove_all_actions: manage_role_permission - - add_members: add_role_users_permission - - list_members: view_role_users_permission - - check_members_exists: view_role_users_permission - - remove_members: remove_role_users_permission - - remove_all_members: remove_role_users_permission + - add: + permission: manage_role_permission + auth_operation: role_add + - remove: + permission: manage_role_permission + auth_operation: role_remove + - update: + permission: manage_role_permission + auth_operation: role_update + - retrieve: + permission: view_role_users_permission + auth_operation: role_retrieve + - retrieve_all: + permission: view_role_users_permission + auth_operation: role_retrieve_all + - add_actions: + permission: manage_role_permission + auth_operation: role_add_actions + - list_actions: + permission: view_role_users_permission + auth_operation: role_list_actions + - check_actions_exists: + permission: view_role_users_permission + auth_operation: role_check_actions_exists + - remove_actions: + permission: manage_role_permission + auth_operation: role_remove_actions + - remove_all_actions: + permission: manage_role_permission + auth_operation: role_remove_all_actions + - add_members: + permission: add_role_users_permission + auth_operation: role_add_members + - list_members: + permission: view_role_users_permission + auth_operation: role_list_members + - check_members_exists: + permission: view_role_users_permission + auth_operation: role_check_members_exists + - remove_members: + permission: remove_role_users_permission + auth_operation: role_remove_members + - remove_all_members: + permission: remove_role_users_permission + auth_operation: role_remove_all_members groups: operations: - - view: read_permission - - update: update_permission - - update_tags: update_permission - - enable: update_permission - - disable: update_permission - - delete: delete_permission - - retrieve_group_hierarchy: read_permission - - add_parent_group: set_parent_permission - - remove_parent_group: set_parent_permission - - add_children_groups: set_child_permission - - remove_children_groups: set_child_permission - - remove_all_children_groups: set_child_permission - - list_children_groups: read_permission - - set_child_client: set_child_permission - - remove_child_client: set_child_permission - - set_child_channel: set_child_permission - - remove_child_channel: set_child_permission + - view: + permission: read_permission + auth_operation: group_view + - update: + permission: update_permission + auth_operation: group_update + - update_tags: + permission: update_permission + auth_operation: group_update_tags + - enable: + permission: update_permission + auth_operation: group_enable + - disable: + permission: update_permission + auth_operation: group_disable + - delete: + permission: delete_permission + auth_operation: group_delete + - retrieve_group_hierarchy: + permission: read_permission + auth_operation: group_retrieve_hierarchy + - add_parent_group: + permission: set_parent_permission + auth_operation: group_add_parent_group + - remove_parent_group: + permission: set_parent_permission + auth_operation: group_remove_parent_group + - add_children_groups: + permission: set_child_permission + auth_operation: group_add_children_groups + - remove_children_groups: + permission: set_child_permission + auth_operation: group_remove_children_groups + - remove_all_children_groups: + permission: set_child_permission + auth_operation: group_remove_all_children_groups + - list_children_groups: + permission: read_permission + auth_operation: group_list_children_groups + - set_child_client: + permission: set_child_permission + auth_operation: group_set_child_client + - remove_child_client: + permission: set_child_permission + auth_operation: group_remove_child_client + - set_child_channel: + permission: set_child_permission + auth_operation: group_set_child_channel + - remove_child_channel: + permission: set_child_permission + auth_operation: group_remove_child_channel roles_operations: - - add: manage_role_permission - - remove: manage_role_permission - - update: manage_role_permission - - retrieve: view_role_users_permission - - retrieve_all: view_role_users_permission - - add_actions: manage_role_permission - - list_actions: view_role_users_permission - - check_actions_exists: view_role_users_permission - - remove_actions: manage_role_permission - - remove_all_actions: manage_role_permission - - add_members: add_role_users_permission - - list_members: view_role_users_permission - - check_members_exists: view_role_users_permission - - remove_members: remove_role_users_permission - - remove_all_members: remove_role_users_permission + - add: + permission: manage_role_permission + auth_operation: role_add + - remove: + permission: manage_role_permission + auth_operation: role_remove + - update: + permission: manage_role_permission + auth_operation: role_update + - retrieve: + permission: view_role_users_permission + auth_operation: role_retrieve + - retrieve_all: + permission: view_role_users_permission + auth_operation: role_retrieve_all + - add_actions: + permission: manage_role_permission + auth_operation: role_add_actions + - list_actions: + permission: view_role_users_permission + auth_operation: role_list_actions + - check_actions_exists: + permission: view_role_users_permission + auth_operation: role_check_actions_exists + - remove_actions: + permission: manage_role_permission + auth_operation: role_remove_actions + - remove_all_actions: + permission: manage_role_permission + auth_operation: role_remove_all_actions + - add_members: + permission: add_role_users_permission + auth_operation: role_add_members + - list_members: + permission: view_role_users_permission + auth_operation: role_list_members + - check_members_exists: + permission: view_role_users_permission + auth_operation: role_check_members_exists + - remove_members: + permission: remove_role_users_permission + auth_operation: role_remove_members + - remove_all_members: + permission: remove_role_users_permission + auth_operation: role_remove_all_members domains: operations: @@ -108,12 +274,24 @@ domains: - list_invitation: membership_permission - list_domain_invitation: manage_role_permission - delete_invitation: manage_role_permission - - create_clients: client_create_permission - - list_clients: client_read_permission - - create_channels: channel_create_permission - - list_channels: channel_read_permission - - create_groups: group_create_permission - - list_groups: group_read_permission + - create_clients: + permission: client_create_permission + auth_operation: client_create + - list_clients: + permission: client_read_permission + auth_operation: client_list + - create_channels: + permission: channel_create_permission + auth_operation: channel_create + - list_channels: + permission: channel_read_permission + auth_operation: channel_list + - create_groups: + permission: group_create_permission + auth_operation: group_create + - list_groups: + permission: group_read_permission + auth_operation: group_list roles_operations: - add: manage_role_permission - remove: manage_role_permission @@ -130,5 +308,3 @@ domains: - check_members_exists: view_role_users_permission - remove_members: remove_role_users_permission - remove_all_members: remove_role_users_permission - - diff --git a/domains/middleware/authorization.go b/domains/middleware/authorization.go index c631250d74..6de505239d 100644 --- a/domains/middleware/authorization.go +++ b/domains/middleware/authorization.go @@ -29,16 +29,17 @@ type authorizationMiddleware struct { authz smqauthz.Authorization entitiesOps permissions.EntitiesOperations[permissions.Operation] rOps permissions.Operations[permissions.RoleOperation] + authOps map[string]auth.Operation rolemgr.RoleManagerAuthorizationMiddleware } // NewAuthorization adds authorization to the domains service. -func NewAuthorization(entityType string, svc domains.Service, authz smqauthz.Authorization, entitiesOps permissions.EntitiesOperations[permissions.Operation], domainRoleOps permissions.Operations[permissions.RoleOperation]) (domains.Service, error) { +func NewAuthorization(entityType string, svc domains.Service, authz smqauthz.Authorization, entitiesOps permissions.EntitiesOperations[permissions.Operation], authOps map[string]auth.Operation, domainRoleOps permissions.Operations[permissions.RoleOperation]) (domains.Service, error) { if err := entitiesOps.Validate(); err != nil { return &authorizationMiddleware{}, err } - ram, err := rolemgr.NewAuthorization(entityType, svc, authz, domainRoleOps) + ram, err := rolemgr.NewAuthorization(entityType, svc, authz, domainRoleOps, authOps) if err != nil { return &authorizationMiddleware{}, err } @@ -47,6 +48,7 @@ func NewAuthorization(entityType string, svc domains.Service, authz smqauthz.Aut authz: authz, entitiesOps: entitiesOps, rOps: domainRoleOps, + authOps: authOps, RoleManagerAuthorizationMiddleware: ram, }, nil } diff --git a/groups/middleware/authorization.go b/groups/middleware/authorization.go index 51884fe076..df9c2d94b5 100644 --- a/groups/middleware/authorization.go +++ b/groups/middleware/authorization.go @@ -47,6 +47,7 @@ type authorizationMiddleware struct { repo groups.Repository authz smqauthz.Authorization entitiesOps permissions.EntitiesOperations[permissions.Operation] + authOps map[string]auth.Operation rolemgr.RoleManagerAuthorizationMiddleware } @@ -57,12 +58,13 @@ func NewAuthorization( authz smqauthz.Authorization, repo groups.Repository, entitiesOps permissions.EntitiesOperations[permissions.Operation], + authOps map[string]auth.Operation, roleOps permissions.Operations[permissions.RoleOperation], ) (groups.Service, error) { if err := entitiesOps.Validate(); err != nil { return nil, err } - ram, err := rolemgr.NewAuthorization(entityType, svc, authz, roleOps) + ram, err := rolemgr.NewAuthorization(policies.GroupType, svc, authz, roleOps, authOps) if err != nil { return nil, err } @@ -72,6 +74,7 @@ func NewAuthorization( authz: authz, repo: repo, entitiesOps: entitiesOps, + authOps: authOps, RoleManagerAuthorizationMiddleware: ram, }, nil } @@ -385,7 +388,7 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, session authn. pr.TokenType = session.Type pr.UserID = session.UserID pr.PatID = session.PatID - pr.OptionalDomainID = session.DomainID + pr.DomainID = session.DomainID perm, err := am.entitiesOps.GetPermission(entityType, op) if err != nil { @@ -397,32 +400,12 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, session authn. pr.EntityType = auth.GroupsType pr.EntityID = pr.Object - switch op { - case groups.OpViewGroup: - pr.Operation = auth.ReadOp - case groups.OpListUserGroups, - groups.OpRetrieveGroupHierarchy, - groups.OpListChildrenGroups, - domains.OpListDomainGroups: - pr.Operation = auth.ListOp - if op == domains.OpListDomainGroups { + opName := am.entitiesOps.OperationName(entityType, op) + if authOp, ok := am.authOps[opName]; ok { + pr.Operation = authOp + if op == domains.OpListDomainGroups || op == domains.OpCreateDomainGroups { pr.EntityID = auth.AnyIDs } - case groups.OpUpdateGroup, - groups.OpUpdateGroupTags, - groups.OpEnableGroup, - groups.OpDisableGroup, - groups.OpAddParentGroup, - groups.OpAddChildrenGroups: - pr.Operation = auth.UpdateOp - case groups.OpDeleteGroup, - groups.OpRemoveParentGroup, - groups.OpRemoveChildrenGroups, - groups.OpRemoveAllChildrenGroups: - pr.Operation = auth.DeleteOp - case domains.OpCreateDomainGroups: - pr.Operation = auth.CreateOp - pr.EntityID = auth.AnyIDs } } diff --git a/internal/proto/auth/v1/auth.proto b/internal/proto/auth/v1/auth.proto index d1b5940820..814e44645e 100644 --- a/internal/proto/auth/v1/auth.proto +++ b/internal/proto/auth/v1/auth.proto @@ -19,39 +19,39 @@ message AuthNReq { } message AuthNRes { - string id = 1; // token id - string user_id = 2; // user id - uint32 user_role = 3; // user role - bool verified = 4; // verified user - uint32 token_type = 5; // token type + string id = 1; + string user_id = 2; + uint32 user_role = 3; + bool verified = 4; + uint32 token_type = 5; } message PolicyReq { - uint32 token_type = 1; // Token type - string domain = 2; // Domain - string subject_type = 3; // Client or User - string subject_kind = 4; // ID or Token - string subject_relation = 5; // Subject relation - string subject = 6; // Subject value - string relation = 7; // Relation to filter - string permission = 8; // Action - string object = 9; // Object ID - string object_type = 10; // Client, User, Group + string domain = 1; + string subject_type = 2; + string subject_kind = 3; + string subject_relation = 4; + string subject = 5; + string relation = 6; + string permission = 7; + string object = 8; + string object_type = 9; } message PATReq { - string user_id = 1; // User id (PAT) - string pat_id = 2; // Pat id - uint32 entity_type = 3; // Entity type (PAT) - string optional_domain_id = 4; // Optional domain id (PAT) - uint32 operation = 5; // Operation (PAT) - string entity_id = 6; // EntityID (PAT) + string user_id = 1; + string pat_id = 2; + uint32 entity_type = 3; + string domain_id = 4; + uint32 operation = 5; + string entity_id = 6; } message AuthZReq { + uint32 token_type = 1; oneof auth_type { - PolicyReq policy = 1; // Policy-based authorization - PATReq pat = 2; // PAT authorization + PolicyReq policy = 2; + PATReq pat = 3; } } diff --git a/pkg/authn/authsvc/authn.go b/pkg/authn/authsvc/authn.go index 68665328ff..c3a7e81621 100644 --- a/pkg/authn/authsvc/authn.go +++ b/pkg/authn/authsvc/authn.go @@ -15,6 +15,10 @@ import ( grpchealth "google.golang.org/grpc/health/grpc_health_v1" ) +const patPrefix = "pat_" + +var errTokenTypeMismatch = errors.NewAuthNError("token type mismatch: token format does not match the service-returned token type") + type authentication struct { authSvcClient grpcAuthV1.AuthServiceClient } @@ -44,9 +48,20 @@ func (a authentication) Authenticate(ctx context.Context, token string) (authn.S return authn.Session{}, errors.Wrap(errors.ErrAuthentication, err) } - if strings.HasPrefix(token, authn.PatPrefix) { - return authn.Session{Type: authn.PersonalAccessToken, PatID: res.GetId(), UserID: res.GetUserId(), Role: authn.Role(res.GetUserRole())}, nil + isPAT := strings.HasPrefix(token, patPrefix) + tokenType := authn.TokenType(res.GetTokenType()) + + if isPAT && tokenType != authn.PersonalAccessToken { + return authn.Session{}, errTokenTypeMismatch + } + if !isPAT && tokenType == authn.PersonalAccessToken { + return authn.Session{}, errTokenTypeMismatch } - return authn.Session{Type: authn.AccessToken, UserID: res.GetUserId(), Role: authn.Role(res.GetUserRole()), Verified: res.GetVerified()}, nil + switch tokenType { + case authn.PersonalAccessToken: + return authn.Session{Type: authn.PersonalAccessToken, PatID: res.GetId(), UserID: res.GetUserId(), Role: authn.Role(res.GetUserRole())}, nil + default: + return authn.Session{Type: authn.AccessToken, UserID: res.GetUserId(), Role: authn.Role(res.GetUserRole()), Verified: res.GetVerified()}, nil + } } diff --git a/pkg/authn/middleware.go b/pkg/authn/middleware.go index 6d24db625a..87ccb0c806 100644 --- a/pkg/authn/middleware.go +++ b/pkg/authn/middleware.go @@ -11,8 +11,8 @@ import ( "strconv" apiutil "github.com/absmach/supermq/api/http/util" - "github.com/absmach/supermq/auth" "github.com/absmach/supermq/pkg/errors" + "github.com/absmach/supermq/pkg/policies" "github.com/go-chi/chi/v5" ) @@ -154,7 +154,7 @@ func (a *authnMiddleware) Middleware() func(http.Handler) http.Handler { case AdminRole: resp.DomainUserID = resp.UserID case UserRole: - resp.DomainUserID = auth.EncodeDomainUserID(domain, resp.UserID) + resp.DomainUserID = policies.EncodeDomainUserID(domain, resp.UserID) } } diff --git a/pkg/authz/authsvc/authz.go b/pkg/authz/authsvc/authz.go index f0651cf704..85f0c65345 100644 --- a/pkg/authz/authsvc/authz.go +++ b/pkg/authz/authsvc/authz.go @@ -48,28 +48,6 @@ func NewAuthorization(ctx context.Context, cfg grpcclient.Config, domainsAuthz p } func (a authorization) Authorize(ctx context.Context, pr authz.PolicyReq) error { - if pr.PatID != "" && pr.TokenType == authn.PersonalAccessToken { - req := grpcAuthV1.AuthZReq{ - AuthType: &grpcAuthV1.AuthZReq_Pat{ - Pat: &grpcAuthV1.PATReq{ - UserId: pr.UserID, - PatId: pr.PatID, - EntityType: uint32(pr.EntityType), - OptionalDomainId: pr.OptionalDomainID, - Operation: uint32(pr.Operation), - EntityId: pr.EntityID, - }, - }, - } - res, err := a.authSvcClient.Authorize(ctx, &req) - if err != nil { - return errors.Wrap(errors.ErrAuthorization, err) - } - if !res.GetAuthorized() { - return errors.ErrAuthorization - } - } - if pr.SubjectType == policies.UserType && (pr.ObjectType == policies.GroupType || pr.ObjectType == policies.ClientType || pr.ObjectType == policies.DomainType) { domainID := pr.Domain if domainID == "" { @@ -83,7 +61,31 @@ func (a authorization) Authorize(ctx context.Context, pr authz.PolicyReq) error } } + if pr.PatID != "" && pr.TokenType == authn.PersonalAccessToken { + patReq := grpcAuthV1.AuthZReq{ + TokenType: uint32(pr.TokenType), + AuthType: &grpcAuthV1.AuthZReq_Pat{ + Pat: &grpcAuthV1.PATReq{ + UserId: pr.UserID, + PatId: pr.PatID, + EntityType: uint32(pr.EntityType), + DomainId: pr.DomainID, + Operation: uint32(pr.Operation), + EntityId: pr.EntityID, + }, + }, + } + patRes, err := a.authSvcClient.Authorize(ctx, &patReq) + if err != nil { + return errors.Wrap(errors.ErrAuthorization, err) + } + if !patRes.GetAuthorized() { + return errors.ErrAuthorization + } + } + req := grpcAuthV1.AuthZReq{ + TokenType: uint32(pr.TokenType), AuthType: &grpcAuthV1.AuthZReq_Policy{ Policy: &grpcAuthV1.PolicyReq{ Domain: pr.Domain, diff --git a/pkg/authz/authz.go b/pkg/authz/authz.go index ca3b9ac02e..281f4bd82e 100644 --- a/pkg/authz/authz.go +++ b/pkg/authz/authz.go @@ -50,12 +50,12 @@ type PolicyReq struct { Permission string `json:"permission,omitempty"` // PAT authorization fields - UserID string `json:"user_id,omitempty"` // UserID who owns the PAT - PatID string `json:"pat_id,omitempty"` // PAT ID - EntityType auth.EntityType `json:"entity_type,omitempty"` // Entity type - OptionalDomainID string `json:"optional_domainID,omitempty"` // Optional domain ID for PAT scope checking - Operation auth.Operation `json:"operation,omitempty"` // Operation type - EntityID string `json:"entityID,omitempty"` // Entity ID + UserID string `json:"user_id,omitempty"` // UserID who owns the PAT + PatID string `json:"pat_id,omitempty"` // PAT ID + EntityType auth.EntityType `json:"entity_type,omitempty"` // Entity type + DomainID string `json:"domain_id,omitempty"` // Domain ID for PAT scope checking + Operation auth.Operation `json:"operation,omitempty"` // Operation type + EntityID string `json:"entity_id,omitempty"` // Entity ID } // Authz is supermq authorization library. diff --git a/pkg/permissions/config.go b/pkg/permissions/config.go index 7b06347c16..5734b75064 100644 --- a/pkg/permissions/config.go +++ b/pkg/permissions/config.go @@ -15,8 +15,8 @@ type PermissionConfig struct { } type EntityPermissions struct { - Operations []map[string]string `yaml:"operations"` - RolesOperations []map[string]string `yaml:"roles_operations"` + Operations []map[string]interface{} `yaml:"operations"` + RolesOperations []map[string]interface{} `yaml:"roles_operations"` } func ParsePermissionsFile(filePath string) (*PermissionConfig, error) { @@ -41,44 +41,73 @@ func (pc *PermissionConfig) GetEntityPermissions(entityType string) (map[string] operations := make(map[string]Permission) for _, op := range entityPerms.Operations { - for name, perm := range op { - operations[name] = Permission(perm) + for name, value := range op { + perm := extractPermission(value) + if perm != "" { + operations[name] = Permission(perm) + } } } rolesOperations := make(map[string]Permission) for _, op := range entityPerms.RolesOperations { - for name, perm := range op { - rolesOperations[name] = Permission(perm) + for name, value := range op { + perm := extractPermission(value) + if perm != "" { + rolesOperations[name] = Permission(perm) + } } } return operations, rolesOperations, nil } -func BuildEntitiesPermissions( - config *PermissionConfig, - entityTypes []string, - operationDetailsFuncs map[string]func() map[Operation]OperationDetails, -) (EntitiesPermission, EntitiesOperationDetails[Operation], error) { - entitiesPermission := make(EntitiesPermission) - entitiesOperationDetails := make(EntitiesOperationDetails[Operation]) - - for _, entityType := range entityTypes { - ops, _, err := config.GetEntityPermissions(entityType) - if err != nil { - return nil, nil, fmt.Errorf("failed to get permissions for %s: %w", entityType, err) - } +func (pc *PermissionConfig) GetAuthOperations(entityType string) (map[string]string, map[string]string, error) { + entityPerms, ok := pc.Entities[entityType] + if !ok { + return nil, nil, fmt.Errorf("entity type %s not found in permissions file", entityType) + } - entitiesPermission[entityType] = ops + operations := make(map[string]string) + for _, op := range entityPerms.Operations { + for name, value := range op { + authOp := extractAuthOperation(value) + if authOp != "" { + operations[name] = authOp + } + } + } - detailsFunc, ok := operationDetailsFuncs[entityType] - if !ok { - return nil, nil, fmt.Errorf("operation details function not found for entity type %s", entityType) + rolesOperations := make(map[string]string) + for _, op := range entityPerms.RolesOperations { + for name, value := range op { + authOp := extractAuthOperation(value) + if authOp != "" { + rolesOperations[name] = authOp + } } + } + + return operations, rolesOperations, nil +} - entitiesOperationDetails[entityType] = detailsFunc() +func extractPermission(value interface{}) string { + switch v := value.(type) { + case string: + return v + case map[string]interface{}: + if perm, ok := v["permission"].(string); ok { + return perm + } } + return "" +} - return entitiesPermission, entitiesOperationDetails, nil +func extractAuthOperation(value interface{}) string { + if m, ok := value.(map[string]interface{}); ok { + if authOp, ok := m["auth_operation"].(string); ok { + return authOp + } + } + return "" } diff --git a/pkg/permissions/operations.go b/pkg/permissions/operations.go index 4ed69268dc..fc2c29ae3b 100644 --- a/pkg/permissions/operations.go +++ b/pkg/permissions/operations.go @@ -35,6 +35,7 @@ type OperationName[K OperationKey] map[K]string type OperationDetails struct { Name string PermissionRequired bool + AuthOperation string } type operations[K OperationKey] struct { diff --git a/pkg/policies/service.go b/pkg/policies/service.go index 9343110c23..dc5a6db079 100644 --- a/pkg/policies/service.go +++ b/pkg/policies/service.go @@ -58,8 +58,8 @@ type Policy struct { PatID string `json:"pat_id,omitempty"` // EntityType contains the entity type for PAT authorization. EntityType uint32 `json:"entity_type,omitempty"` - // OptionalDomainID contains the optional domain ID for PAT scope checking. - OptionalDomainID string `json:"optional_domain_id,omitempty"` + // DomainID contains the domain ID for PAT scope checking. + DomainID string `json:"domain_id,omitempty"` // Operation contains the operation type for PAT authorization. Operation uint32 `json:"operation,omitempty"` // EntityID contains the entity ID for PAT authorization. diff --git a/pkg/roles/rolemanager/middleware/authorization.go b/pkg/roles/rolemanager/middleware/authorization.go index 6e03ff14e6..450aa085ad 100644 --- a/pkg/roles/rolemanager/middleware/authorization.go +++ b/pkg/roles/rolemanager/middleware/authorization.go @@ -6,6 +6,7 @@ package middleware import ( "context" + "github.com/absmach/supermq/auth" "github.com/absmach/supermq/pkg/authn" smqauthz "github.com/absmach/supermq/pkg/authz" "github.com/absmach/supermq/pkg/errors" @@ -21,10 +22,11 @@ type RoleManagerAuthorizationMiddleware struct { svc roles.RoleManager authz smqauthz.Authorization ops permissions.Operations[permissions.RoleOperation] + authOps map[string]auth.Operation } // NewAuthorization adds authorization for role related methods to the core service. -func NewAuthorization(entityType string, svc roles.RoleManager, authz smqauthz.Authorization, roleOps permissions.Operations[permissions.RoleOperation]) (RoleManagerAuthorizationMiddleware, error) { +func NewAuthorization(entityType string, svc roles.RoleManager, authz smqauthz.Authorization, roleOps permissions.Operations[permissions.RoleOperation], authOps map[string]auth.Operation) (RoleManagerAuthorizationMiddleware, error) { if err := roleOps.Validate(); err != nil { return RoleManagerAuthorizationMiddleware{}, err } @@ -34,13 +36,14 @@ func NewAuthorization(entityType string, svc roles.RoleManager, authz smqauthz.A svc: svc, authz: authz, ops: roleOps, + authOps: authOps, } return ram, nil } func (ram RoleManagerAuthorizationMiddleware) AddRole(ctx context.Context, session authn.Session, entityID, roleName string, optionalActions []string, optionalMembers []string) (roles.RoleProvision, error) { - if err := ram.authorize(ctx, roles.OpAddRole, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpAddRole, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -57,7 +60,7 @@ func (ram RoleManagerAuthorizationMiddleware) AddRole(ctx context.Context, sessi } func (ram RoleManagerAuthorizationMiddleware) RemoveRole(ctx context.Context, session authn.Session, entityID, roleID string) error { - if err := ram.authorize(ctx, roles.OpRemoveRole, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRemoveRole, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -71,7 +74,7 @@ func (ram RoleManagerAuthorizationMiddleware) RemoveRole(ctx context.Context, se } func (ram RoleManagerAuthorizationMiddleware) UpdateRoleName(ctx context.Context, session authn.Session, entityID, roleID, newRoleName string) (roles.Role, error) { - if err := ram.authorize(ctx, roles.OpUpdateRoleName, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpUpdateRoleName, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -85,7 +88,7 @@ func (ram RoleManagerAuthorizationMiddleware) UpdateRoleName(ctx context.Context } func (ram RoleManagerAuthorizationMiddleware) RetrieveRole(ctx context.Context, session authn.Session, entityID, roleID string) (roles.Role, error) { - if err := ram.authorize(ctx, roles.OpRetrieveRole, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRetrieveRole, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -99,7 +102,7 @@ func (ram RoleManagerAuthorizationMiddleware) RetrieveRole(ctx context.Context, } func (ram RoleManagerAuthorizationMiddleware) RetrieveAllRoles(ctx context.Context, session authn.Session, entityID string, limit, offset uint64) (roles.RolePage, error) { - if err := ram.authorize(ctx, roles.OpRetrieveAllRoles, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRetrieveAllRoles, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -117,7 +120,7 @@ func (ram RoleManagerAuthorizationMiddleware) ListAvailableActions(ctx context.C } func (ram RoleManagerAuthorizationMiddleware) RoleAddActions(ctx context.Context, session authn.Session, entityID, roleID string, actions []string) (ops []string, err error) { - if err := ram.authorize(ctx, roles.OpRoleAddActions, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRoleAddActions, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -132,7 +135,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleAddActions(ctx context.Context } func (ram RoleManagerAuthorizationMiddleware) RoleListActions(ctx context.Context, session authn.Session, entityID, roleID string) ([]string, error) { - if err := ram.authorize(ctx, roles.OpRoleListActions, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRoleListActions, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -147,7 +150,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleListActions(ctx context.Contex } func (ram RoleManagerAuthorizationMiddleware) RoleCheckActionsExists(ctx context.Context, session authn.Session, entityID, roleID string, actions []string) (bool, error) { - if err := ram.authorize(ctx, roles.OpRoleCheckActionsExists, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRoleCheckActionsExists, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -161,7 +164,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleCheckActionsExists(ctx context } func (ram RoleManagerAuthorizationMiddleware) RoleRemoveActions(ctx context.Context, session authn.Session, entityID, roleID string, actions []string) (err error) { - if err := ram.authorize(ctx, roles.OpRoleRemoveActions, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRoleRemoveActions, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -176,7 +179,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleRemoveActions(ctx context.Cont } func (ram RoleManagerAuthorizationMiddleware) RoleRemoveAllActions(ctx context.Context, session authn.Session, entityID, roleID string) error { - if err := ram.authorize(ctx, roles.OpRoleRemoveAllActions, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRoleRemoveAllActions, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -190,7 +193,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleRemoveAllActions(ctx context.C } func (ram RoleManagerAuthorizationMiddleware) RoleAddMembers(ctx context.Context, session authn.Session, entityID, roleID string, members []string) ([]string, error) { - if err := ram.authorize(ctx, roles.OpRoleAddMembers, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRoleAddMembers, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -208,7 +211,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleAddMembers(ctx context.Context } func (ram RoleManagerAuthorizationMiddleware) RoleListMembers(ctx context.Context, session authn.Session, entityID, roleID string, limit, offset uint64) (roles.MembersPage, error) { - if err := ram.authorize(ctx, roles.OpRoleListMembers, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRoleListMembers, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -222,7 +225,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleListMembers(ctx context.Contex } func (ram RoleManagerAuthorizationMiddleware) RoleCheckMembersExists(ctx context.Context, session authn.Session, entityID, roleID string, members []string) (bool, error) { - if err := ram.authorize(ctx, roles.OpRoleCheckMembersExists, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRoleCheckMembersExists, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -236,7 +239,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleCheckMembersExists(ctx context } func (ram RoleManagerAuthorizationMiddleware) RoleRemoveAllMembers(ctx context.Context, session authn.Session, entityID, roleID string) (err error) { - if err := ram.authorize(ctx, roles.OpRoleRemoveAllMembers, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRoleRemoveAllMembers, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -250,7 +253,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleRemoveAllMembers(ctx context.C } func (ram RoleManagerAuthorizationMiddleware) ListEntityMembers(ctx context.Context, session authn.Session, entityID string, pageQuery roles.MembersRolePageQuery) (roles.MembersRolePage, error) { - if err := ram.authorize(ctx, roles.OpRoleListMembers, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRoleListMembers, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -264,7 +267,7 @@ func (ram RoleManagerAuthorizationMiddleware) ListEntityMembers(ctx context.Cont } func (ram RoleManagerAuthorizationMiddleware) RemoveEntityMembers(ctx context.Context, session authn.Session, entityID string, members []string) error { - if err := ram.authorize(ctx, roles.OpRoleRemoveAllMembers, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRoleRemoveAllMembers, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -278,7 +281,7 @@ func (ram RoleManagerAuthorizationMiddleware) RemoveEntityMembers(ctx context.Co } func (ram RoleManagerAuthorizationMiddleware) RoleRemoveMembers(ctx context.Context, session authn.Session, entityID, roleID string, members []string) (err error) { - if err := ram.authorize(ctx, roles.OpRoleRemoveMembers, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, session, roles.OpRoleRemoveMembers, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -291,7 +294,12 @@ func (ram RoleManagerAuthorizationMiddleware) RoleRemoveMembers(ctx context.Cont return ram.svc.RoleRemoveMembers(ctx, session, entityID, roleID, members) } -func (ram RoleManagerAuthorizationMiddleware) authorize(ctx context.Context, op permissions.RoleOperation, pr smqauthz.PolicyReq) error { +func (ram RoleManagerAuthorizationMiddleware) authorize(ctx context.Context, session authn.Session, op permissions.RoleOperation, pr smqauthz.PolicyReq) error { + pr.TokenType = session.Type + pr.UserID = session.UserID + pr.PatID = session.PatID + pr.DomainID = session.DomainID + perm, err := ram.ops.GetPermission(op) if err != nil { return err @@ -299,6 +307,24 @@ func (ram RoleManagerAuthorizationMiddleware) authorize(ctx context.Context, op pr.Permission = perm.String() + if pr.PatID != "" && pr.TokenType == authn.PersonalAccessToken { + pr.EntityID = pr.Object + + switch ram.entityType { + case policies.ClientType: + pr.EntityType = auth.ClientsType + case policies.ChannelType: + pr.EntityType = auth.ChannelsType + case policies.GroupType: + pr.EntityType = auth.GroupsType + } + + opName := ram.ops.OperationName(op) + if authOp, ok := ram.authOps[opName]; ok { + pr.Operation = authOp + } + } + if err := ram.authz.Authorize(ctx, pr); err != nil { return errors.Wrap(errors.ErrAuthorization, err) }