NOISSUE - Seperate PAT from policy (#3330)

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>
Co-authored-by: Arvindh <arvindh91@gmail.com>
This commit is contained in:
Steve Munene
2026-02-26 18:35:47 +03:00
committed by GitHub
parent 175f0e08ab
commit 15a6c026e9
27 changed files with 826 additions and 431 deletions
+145 -37
View File
@@ -147,11 +147,6 @@ type PolicyReq struct {
Permission string `protobuf:"bytes,7,opt,name=permission,proto3" json:"permission,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"` 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"` ObjectType string `protobuf:"bytes,9,opt,name=object_type,json=objectType,proto3" json:"object_type,omitempty"`
PatId string `protobuf:"bytes,10,opt,name=pat_id,json=patId,proto3" json:"pat_id,omitempty"`
Operation string `protobuf:"bytes,11,opt,name=operation,proto3" json:"operation,omitempty"`
UserId string `protobuf:"bytes,12,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
EntityId string `protobuf:"bytes,13,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"`
EntityType string `protobuf:"bytes,14,opt,name=entity_type,json=entityType,proto3" json:"entity_type,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -249,41 +244,142 @@ func (x *PolicyReq) GetObjectType() string {
return "" return ""
} }
func (x *PolicyReq) GetPatId() string { type PATReq struct {
state protoimpl.MessageState `protogen:"open.v1"`
PatId string `protobuf:"bytes,1,opt,name=pat_id,json=patId,proto3" json:"pat_id,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
Operation string `protobuf:"bytes,3,opt,name=operation,proto3" json:"operation,omitempty"`
UserId string `protobuf:"bytes,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
EntityId string `protobuf:"bytes,5,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"`
EntityType string `protobuf:"bytes,6,opt,name=entity_type,json=entityType,proto3" json:"entity_type,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *PATReq) Reset() {
*x = PATReq{}
mi := &file_auth_v1_auth_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *PATReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PATReq) ProtoMessage() {}
func (x *PATReq) ProtoReflect() protoreflect.Message {
mi := &file_auth_v1_auth_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PATReq.ProtoReflect.Descriptor instead.
func (*PATReq) Descriptor() ([]byte, []int) {
return file_auth_v1_auth_proto_rawDescGZIP(), []int{3}
}
func (x *PATReq) GetPatId() string {
if x != nil { if x != nil {
return x.PatId return x.PatId
} }
return "" return ""
} }
func (x *PolicyReq) GetOperation() string { func (x *PATReq) GetDomain() string {
if x != nil {
return x.Domain
}
return ""
}
func (x *PATReq) GetOperation() string {
if x != nil { if x != nil {
return x.Operation return x.Operation
} }
return "" return ""
} }
func (x *PolicyReq) GetUserId() string { func (x *PATReq) GetUserId() string {
if x != nil { if x != nil {
return x.UserId return x.UserId
} }
return "" return ""
} }
func (x *PolicyReq) GetEntityId() string { func (x *PATReq) GetEntityId() string {
if x != nil { if x != nil {
return x.EntityId return x.EntityId
} }
return "" return ""
} }
func (x *PolicyReq) GetEntityType() string { func (x *PATReq) GetEntityType() string {
if x != nil { if x != nil {
return x.EntityType return x.EntityType
} }
return "" return ""
} }
type AuthZReq struct {
state protoimpl.MessageState `protogen:"open.v1"`
PolicyReq *PolicyReq `protobuf:"bytes,1,opt,name=policy_req,json=policyReq,proto3" json:"policy_req,omitempty"`
PatReq *PATReq `protobuf:"bytes,2,opt,name=pat_req,json=patReq,proto3,oneof" json:"pat_req,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AuthZReq) Reset() {
*x = AuthZReq{}
mi := &file_auth_v1_auth_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AuthZReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AuthZReq) ProtoMessage() {}
func (x *AuthZReq) ProtoReflect() protoreflect.Message {
mi := &file_auth_v1_auth_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AuthZReq.ProtoReflect.Descriptor instead.
func (*AuthZReq) Descriptor() ([]byte, []int) {
return file_auth_v1_auth_proto_rawDescGZIP(), []int{4}
}
func (x *AuthZReq) GetPolicyReq() *PolicyReq {
if x != nil {
return x.PolicyReq
}
return nil
}
func (x *AuthZReq) GetPatReq() *PATReq {
if x != nil {
return x.PatReq
}
return nil
}
type AuthZRes struct { type AuthZRes struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Authorized bool `protobuf:"varint,1,opt,name=authorized,proto3" json:"authorized,omitempty"` Authorized bool `protobuf:"varint,1,opt,name=authorized,proto3" json:"authorized,omitempty"`
@@ -294,7 +390,7 @@ type AuthZRes struct {
func (x *AuthZRes) Reset() { func (x *AuthZRes) Reset() {
*x = AuthZRes{} *x = AuthZRes{}
mi := &file_auth_v1_auth_proto_msgTypes[3] mi := &file_auth_v1_auth_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -306,7 +402,7 @@ func (x *AuthZRes) String() string {
func (*AuthZRes) ProtoMessage() {} func (*AuthZRes) ProtoMessage() {}
func (x *AuthZRes) ProtoReflect() protoreflect.Message { func (x *AuthZRes) ProtoReflect() protoreflect.Message {
mi := &file_auth_v1_auth_proto_msgTypes[3] mi := &file_auth_v1_auth_proto_msgTypes[5]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -319,7 +415,7 @@ func (x *AuthZRes) ProtoReflect() protoreflect.Message {
// Deprecated: Use AuthZRes.ProtoReflect.Descriptor instead. // Deprecated: Use AuthZRes.ProtoReflect.Descriptor instead.
func (*AuthZRes) Descriptor() ([]byte, []int) { func (*AuthZRes) Descriptor() ([]byte, []int) {
return file_auth_v1_auth_proto_rawDescGZIP(), []int{3} return file_auth_v1_auth_proto_rawDescGZIP(), []int{5}
} }
func (x *AuthZRes) GetAuthorized() bool { func (x *AuthZRes) GetAuthorized() bool {
@@ -347,7 +443,7 @@ const file_auth_v1_auth_proto_rawDesc = "" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" +
"\auser_id\x18\x02 \x01(\tR\x06userId\x12\x1b\n" + "\auser_id\x18\x02 \x01(\tR\x06userId\x12\x1b\n" +
"\tuser_role\x18\x03 \x01(\rR\buserRole\x12\x1a\n" + "\tuser_role\x18\x03 \x01(\rR\buserRole\x12\x1a\n" +
"\bverified\x18\x04 \x01(\bR\bverified\"\xaf\x03\n" + "\bverified\x18\x04 \x01(\bR\bverified\"\xa3\x02\n" +
"\tPolicyReq\x12\x16\n" + "\tPolicyReq\x12\x16\n" +
"\x06domain\x18\x01 \x01(\tR\x06domain\x12!\n" + "\x06domain\x18\x01 \x01(\tR\x06domain\x12!\n" +
"\fsubject_type\x18\x02 \x01(\tR\vsubjectType\x12!\n" + "\fsubject_type\x18\x02 \x01(\tR\vsubjectType\x12!\n" +
@@ -360,21 +456,28 @@ const file_auth_v1_auth_proto_rawDesc = "" +
"permission\x12\x16\n" + "permission\x12\x16\n" +
"\x06object\x18\b \x01(\tR\x06object\x12\x1f\n" + "\x06object\x18\b \x01(\tR\x06object\x12\x1f\n" +
"\vobject_type\x18\t \x01(\tR\n" + "\vobject_type\x18\t \x01(\tR\n" +
"objectType\x12\x15\n" + "objectType\"\xac\x01\n" +
"\x06pat_id\x18\n" + "\x06PATReq\x12\x15\n" +
" \x01(\tR\x05patId\x12\x1c\n" + "\x06pat_id\x18\x01 \x01(\tR\x05patId\x12\x16\n" +
"\toperation\x18\v \x01(\tR\toperation\x12\x17\n" + "\x06domain\x18\x02 \x01(\tR\x06domain\x12\x1c\n" +
"\auser_id\x18\f \x01(\tR\x06userId\x12\x1b\n" + "\toperation\x18\x03 \x01(\tR\toperation\x12\x17\n" +
"\tentity_id\x18\r \x01(\tR\bentityId\x12\x1f\n" + "\auser_id\x18\x04 \x01(\tR\x06userId\x12\x1b\n" +
"\ventity_type\x18\x0e \x01(\tR\n" + "\tentity_id\x18\x05 \x01(\tR\bentityId\x12\x1f\n" +
"entityType\":\n" + "\ventity_type\x18\x06 \x01(\tR\n" +
"entityType\"x\n" +
"\bAuthZReq\x121\n" +
"\n" +
"policy_req\x18\x01 \x01(\v2\x12.auth.v1.PolicyReqR\tpolicyReq\x12-\n" +
"\apat_req\x18\x02 \x01(\v2\x0f.auth.v1.PATReqH\x00R\x06patReq\x88\x01\x01B\n" +
"\n" +
"\b_pat_req\":\n" +
"\bAuthZRes\x12\x1e\n" + "\bAuthZRes\x12\x1e\n" +
"\n" + "\n" +
"authorized\x18\x01 \x01(\bR\n" + "authorized\x18\x01 \x01(\bR\n" +
"authorized\x12\x0e\n" + "authorized\x12\x0e\n" +
"\x02id\x18\x02 \x01(\tR\x02id2{\n" + "\x02id\x18\x02 \x01(\tR\x02id2z\n" +
"\vAuthService\x124\n" + "\vAuthService\x123\n" +
"\tAuthorize\x12\x12.auth.v1.PolicyReq\x1a\x11.auth.v1.AuthZRes\"\x00\x126\n" + "\tAuthorize\x12\x11.auth.v1.AuthZReq\x1a\x11.auth.v1.AuthZRes\"\x00\x126\n" +
"\fAuthenticate\x12\x11.auth.v1.AuthNReq\x1a\x11.auth.v1.AuthNRes\"\x00B-Z+github.com/absmach/supermq/api/grpc/auth/v1b\x06proto3" "\fAuthenticate\x12\x11.auth.v1.AuthNReq\x1a\x11.auth.v1.AuthNRes\"\x00B-Z+github.com/absmach/supermq/api/grpc/auth/v1b\x06proto3"
var ( var (
@@ -389,23 +492,27 @@ func file_auth_v1_auth_proto_rawDescGZIP() []byte {
return file_auth_v1_auth_proto_rawDescData return file_auth_v1_auth_proto_rawDescData
} }
var file_auth_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_auth_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_auth_v1_auth_proto_goTypes = []any{ var file_auth_v1_auth_proto_goTypes = []any{
(*AuthNReq)(nil), // 0: auth.v1.AuthNReq (*AuthNReq)(nil), // 0: auth.v1.AuthNReq
(*AuthNRes)(nil), // 1: auth.v1.AuthNRes (*AuthNRes)(nil), // 1: auth.v1.AuthNRes
(*PolicyReq)(nil), // 2: auth.v1.PolicyReq (*PolicyReq)(nil), // 2: auth.v1.PolicyReq
(*AuthZRes)(nil), // 3: auth.v1.AuthZRes (*PATReq)(nil), // 3: auth.v1.PATReq
(*AuthZReq)(nil), // 4: auth.v1.AuthZReq
(*AuthZRes)(nil), // 5: auth.v1.AuthZRes
} }
var file_auth_v1_auth_proto_depIdxs = []int32{ var file_auth_v1_auth_proto_depIdxs = []int32{
2, // 0: auth.v1.AuthService.Authorize:input_type -> auth.v1.PolicyReq 2, // 0: auth.v1.AuthZReq.policy_req:type_name -> auth.v1.PolicyReq
0, // 1: auth.v1.AuthService.Authenticate:input_type -> auth.v1.AuthNReq 3, // 1: auth.v1.AuthZReq.pat_req:type_name -> auth.v1.PATReq
3, // 2: auth.v1.AuthService.Authorize:output_type -> auth.v1.AuthZRes 4, // 2: auth.v1.AuthService.Authorize:input_type -> auth.v1.AuthZReq
1, // 3: auth.v1.AuthService.Authenticate:output_type -> auth.v1.AuthNRes 0, // 3: auth.v1.AuthService.Authenticate:input_type -> auth.v1.AuthNReq
2, // [2:4] is the sub-list for method output_type 5, // 4: auth.v1.AuthService.Authorize:output_type -> auth.v1.AuthZRes
0, // [0:2] is the sub-list for method input_type 1, // 5: auth.v1.AuthService.Authenticate:output_type -> auth.v1.AuthNRes
0, // [0:0] is the sub-list for extension type_name 4, // [4:6] is the sub-list for method output_type
0, // [0:0] is the sub-list for extension extendee 2, // [2:4] is the sub-list for method input_type
0, // [0:0] is the sub-list for field type_name 2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
} }
func init() { file_auth_v1_auth_proto_init() } func init() { file_auth_v1_auth_proto_init() }
@@ -413,13 +520,14 @@ func file_auth_v1_auth_proto_init() {
if File_auth_v1_auth_proto != nil { if File_auth_v1_auth_proto != nil {
return return
} }
file_auth_v1_auth_proto_msgTypes[4].OneofWrappers = []any{}
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_auth_v1_auth_proto_rawDesc), len(file_auth_v1_auth_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_auth_v1_auth_proto_rawDesc), len(file_auth_v1_auth_proto_rawDesc)),
NumEnums: 0, NumEnums: 0,
NumMessages: 4, NumMessages: 6,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },
+6 -6
View File
@@ -33,7 +33,7 @@ const (
// AuthService is a service that provides authentication // AuthService is a service that provides authentication
// and authorization functionalities for SuperMQ services. // and authorization functionalities for SuperMQ services.
type AuthServiceClient interface { type AuthServiceClient interface {
Authorize(ctx context.Context, in *PolicyReq, opts ...grpc.CallOption) (*AuthZRes, error) Authorize(ctx context.Context, in *AuthZReq, opts ...grpc.CallOption) (*AuthZRes, error)
Authenticate(ctx context.Context, in *AuthNReq, opts ...grpc.CallOption) (*AuthNRes, error) Authenticate(ctx context.Context, in *AuthNReq, opts ...grpc.CallOption) (*AuthNRes, error)
} }
@@ -45,7 +45,7 @@ func NewAuthServiceClient(cc grpc.ClientConnInterface) AuthServiceClient {
return &authServiceClient{cc} return &authServiceClient{cc}
} }
func (c *authServiceClient) Authorize(ctx context.Context, in *PolicyReq, opts ...grpc.CallOption) (*AuthZRes, error) { func (c *authServiceClient) Authorize(ctx context.Context, in *AuthZReq, opts ...grpc.CallOption) (*AuthZRes, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AuthZRes) out := new(AuthZRes)
err := c.cc.Invoke(ctx, AuthService_Authorize_FullMethodName, in, out, cOpts...) err := c.cc.Invoke(ctx, AuthService_Authorize_FullMethodName, in, out, cOpts...)
@@ -72,7 +72,7 @@ func (c *authServiceClient) Authenticate(ctx context.Context, in *AuthNReq, opts
// AuthService is a service that provides authentication // AuthService is a service that provides authentication
// and authorization functionalities for SuperMQ services. // and authorization functionalities for SuperMQ services.
type AuthServiceServer interface { type AuthServiceServer interface {
Authorize(context.Context, *PolicyReq) (*AuthZRes, error) Authorize(context.Context, *AuthZReq) (*AuthZRes, error)
Authenticate(context.Context, *AuthNReq) (*AuthNRes, error) Authenticate(context.Context, *AuthNReq) (*AuthNRes, error)
mustEmbedUnimplementedAuthServiceServer() mustEmbedUnimplementedAuthServiceServer()
} }
@@ -84,7 +84,7 @@ type AuthServiceServer interface {
// pointer dereference when methods are called. // pointer dereference when methods are called.
type UnimplementedAuthServiceServer struct{} type UnimplementedAuthServiceServer struct{}
func (UnimplementedAuthServiceServer) Authorize(context.Context, *PolicyReq) (*AuthZRes, error) { func (UnimplementedAuthServiceServer) Authorize(context.Context, *AuthZReq) (*AuthZRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented") return nil, status.Errorf(codes.Unimplemented, "method Authorize not implemented")
} }
func (UnimplementedAuthServiceServer) Authenticate(context.Context, *AuthNReq) (*AuthNRes, error) { func (UnimplementedAuthServiceServer) Authenticate(context.Context, *AuthNReq) (*AuthNRes, error) {
@@ -112,7 +112,7 @@ func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) {
} }
func _AuthService_Authorize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _AuthService_Authorize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PolicyReq) in := new(AuthZReq)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
@@ -124,7 +124,7 @@ func _AuthService_Authorize_Handler(srv interface{}, ctx context.Context, dec fu
FullMethod: AuthService_Authorize_FullMethodName, FullMethod: AuthService_Authorize_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServiceServer).Authorize(ctx, req.(*PolicyReq)) return srv.(AuthServiceServer).Authorize(ctx, req.(*AuthZReq))
} }
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
+48 -30
View File
@@ -70,27 +70,35 @@ func decodeIdentifyResponse(_ context.Context, grpcRes any) (any, error) {
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()}, nil
} }
func (client authGrpcClient) Authorize(ctx context.Context, req *grpcAuthV1.PolicyReq, _ ...grpc.CallOption) (r *grpcAuthV1.AuthZRes, err error) { func (client authGrpcClient) Authorize(ctx context.Context, req *grpcAuthV1.AuthZReq, _ ...grpc.CallOption) (r *grpcAuthV1.AuthZRes, err error) {
ctx, cancel := context.WithTimeout(ctx, client.timeout) ctx, cancel := context.WithTimeout(ctx, client.timeout)
defer cancel() defer cancel()
var authReqData authReq var authReqData authReq
if req != nil { if req != nil {
authReqData = authReq{ policyReq := req.GetPolicyReq()
Domain: req.GetDomain(), patReq := req.GetPatReq()
SubjectType: req.GetSubjectType(),
Subject: req.GetSubject(), if policyReq != nil {
SubjectKind: req.GetSubjectKind(), authReqData = authReq{
Relation: req.GetRelation(), Domain: policyReq.GetDomain(),
Permission: req.GetPermission(), SubjectType: policyReq.GetSubjectType(),
ObjectType: req.GetObjectType(), Subject: policyReq.GetSubject(),
Object: req.GetObject(), SubjectKind: policyReq.GetSubjectKind(),
UserID: req.GetUserId(), Relation: policyReq.GetRelation(),
PatID: req.GetPatId(), Permission: policyReq.GetPermission(),
EntityType: req.GetEntityType(), ObjectType: policyReq.GetObjectType(),
Operation: req.GetOperation(), Object: policyReq.GetObject(),
EntityID: req.GetEntityId(), }
}
if patReq != nil {
authReqData.UserID = patReq.GetUserId()
authReqData.PatID = patReq.GetPatId()
authReqData.EntityType = patReq.GetEntityType()
authReqData.Operation = patReq.GetOperation()
authReqData.EntityID = patReq.GetEntityId()
} }
} }
@@ -111,19 +119,29 @@ func decodeAuthorizeResponse(_ context.Context, grpcRes any) (any, error) {
func encodeAuthorizeRequest(_ context.Context, grpcReq any) (any, error) { func encodeAuthorizeRequest(_ context.Context, grpcReq any) (any, error) {
req := grpcReq.(authReq) req := grpcReq.(authReq)
return &grpcAuthV1.PolicyReq{ authZReq := &grpcAuthV1.AuthZReq{
Domain: req.Domain, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: req.SubjectType, Domain: req.Domain,
Subject: req.Subject, SubjectType: req.SubjectType,
SubjectKind: req.SubjectKind, Subject: req.Subject,
Relation: req.Relation, SubjectKind: req.SubjectKind,
Permission: req.Permission, Relation: req.Relation,
ObjectType: req.ObjectType, Permission: req.Permission,
Object: req.Object, ObjectType: req.ObjectType,
UserId: req.UserID, Object: req.Object,
PatId: req.PatID, },
EntityType: req.EntityType, }
Operation: req.Operation,
EntityId: req.EntityID, if req.PatID != "" {
}, nil authZReq.PatReq = &grpcAuthV1.PATReq{
PatId: req.PatID,
Domain: req.Domain,
Operation: req.Operation,
UserId: req.UserID,
EntityId: req.EntityID,
EntityType: req.EntityType,
}
}
return authZReq, nil
} }
+17 -6
View File
@@ -35,6 +35,22 @@ func authorizeEndpoint(svc auth.Service) endpoint.Endpoint {
return authorizeRes{}, err return authorizeRes{}, err
} }
var pat *auth.PATAuthz
if req.PatID != "" {
entityType, err := auth.ParseEntityType(req.EntityType)
if err != nil {
return authorizeRes{authorized: false}, err
}
pat = &auth.PATAuthz{
PatID: req.PatID,
UserID: req.UserID,
EntityType: entityType,
EntityID: req.EntityID,
Operation: req.Operation,
Domain: req.Domain,
}
}
err := svc.Authorize(ctx, policies.Policy{ err := svc.Authorize(ctx, policies.Policy{
Domain: req.Domain, Domain: req.Domain,
SubjectType: req.SubjectType, SubjectType: req.SubjectType,
@@ -44,12 +60,7 @@ func authorizeEndpoint(svc auth.Service) endpoint.Endpoint {
Permission: req.Permission, Permission: req.Permission,
ObjectType: req.ObjectType, ObjectType: req.ObjectType,
Object: req.Object, Object: req.Object,
PatID: req.PatID, }, pat)
Operation: req.Operation,
UserID: req.UserID,
EntityType: req.EntityType,
EntityID: req.EntityID,
})
if err != nil { if err != nil {
return authorizeRes{authorized: false}, err return authorizeRes{authorized: false}, err
} }
+135 -101
View File
@@ -129,20 +129,22 @@ func TestAuthorize(t *testing.T) {
cases := []struct { cases := []struct {
desc string desc string
token string token string
authRequest *grpcAuthV1.PolicyReq authRequest *grpcAuthV1.AuthZReq
authResponse *grpcAuthV1.AuthZRes authResponse *grpcAuthV1.AuthZRes
err error err error
}{ }{
{ {
desc: "authorize user with authorized token", desc: "authorize user with authorized token",
token: validToken, token: validToken,
authRequest: &grpcAuthV1.PolicyReq{ authRequest: &grpcAuthV1.AuthZReq{
Subject: id, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: usersType, Subject: id,
Object: authoritiesObj, SubjectType: usersType,
ObjectType: usersType, Object: authoritiesObj,
Relation: memberRelation, ObjectType: usersType,
Permission: adminPermission, Relation: memberRelation,
Permission: adminPermission,
},
}, },
authResponse: &grpcAuthV1.AuthZRes{Authorized: true}, authResponse: &grpcAuthV1.AuthZRes{Authorized: true},
err: nil, err: nil,
@@ -150,13 +152,15 @@ func TestAuthorize(t *testing.T) {
{ {
desc: "authorize user with unauthorized token", desc: "authorize user with unauthorized token",
token: inValidToken, token: inValidToken,
authRequest: &grpcAuthV1.PolicyReq{ authRequest: &grpcAuthV1.AuthZReq{
Subject: id, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: usersType, Subject: id,
Object: authoritiesObj, SubjectType: usersType,
ObjectType: usersType, Object: authoritiesObj,
Relation: memberRelation, ObjectType: usersType,
Permission: adminPermission, Relation: memberRelation,
Permission: adminPermission,
},
}, },
authResponse: &grpcAuthV1.AuthZRes{Authorized: false}, authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: svcerr.ErrAuthorization, err: svcerr.ErrAuthorization,
@@ -164,13 +168,15 @@ func TestAuthorize(t *testing.T) {
{ {
desc: "authorize user with empty subject", desc: "authorize user with empty subject",
token: validToken, token: validToken,
authRequest: &grpcAuthV1.PolicyReq{ authRequest: &grpcAuthV1.AuthZReq{
Subject: "", PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: usersType, Subject: "",
Object: authoritiesObj, SubjectType: usersType,
ObjectType: usersType, Object: authoritiesObj,
Relation: memberRelation, ObjectType: usersType,
Permission: adminPermission, Relation: memberRelation,
Permission: adminPermission,
},
}, },
authResponse: &grpcAuthV1.AuthZRes{Authorized: false}, authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: apiutil.ErrMissingPolicySub, err: apiutil.ErrMissingPolicySub,
@@ -178,13 +184,15 @@ func TestAuthorize(t *testing.T) {
{ {
desc: "authorize user with empty subject type", desc: "authorize user with empty subject type",
token: validToken, token: validToken,
authRequest: &grpcAuthV1.PolicyReq{ authRequest: &grpcAuthV1.AuthZReq{
Subject: id, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: "", Subject: id,
Object: authoritiesObj, SubjectType: "",
ObjectType: usersType, Object: authoritiesObj,
Relation: memberRelation, ObjectType: usersType,
Permission: adminPermission, Relation: memberRelation,
Permission: adminPermission,
},
}, },
authResponse: &grpcAuthV1.AuthZRes{Authorized: false}, authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: apiutil.ErrMissingPolicySub, err: apiutil.ErrMissingPolicySub,
@@ -192,13 +200,15 @@ func TestAuthorize(t *testing.T) {
{ {
desc: "authorize user with empty object", desc: "authorize user with empty object",
token: validToken, token: validToken,
authRequest: &grpcAuthV1.PolicyReq{ authRequest: &grpcAuthV1.AuthZReq{
Subject: id, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: usersType, Subject: id,
Object: "", SubjectType: usersType,
ObjectType: usersType, Object: "",
Relation: memberRelation, ObjectType: usersType,
Permission: adminPermission, Relation: memberRelation,
Permission: adminPermission,
},
}, },
authResponse: &grpcAuthV1.AuthZRes{Authorized: false}, authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: apiutil.ErrMissingPolicyObj, err: apiutil.ErrMissingPolicyObj,
@@ -206,13 +216,15 @@ func TestAuthorize(t *testing.T) {
{ {
desc: "authorize user with empty object type", desc: "authorize user with empty object type",
token: validToken, token: validToken,
authRequest: &grpcAuthV1.PolicyReq{ authRequest: &grpcAuthV1.AuthZReq{
Subject: id, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: usersType, Subject: id,
Object: authoritiesObj, SubjectType: usersType,
ObjectType: "", Object: authoritiesObj,
Relation: memberRelation, ObjectType: "",
Permission: adminPermission, Relation: memberRelation,
Permission: adminPermission,
},
}, },
authResponse: &grpcAuthV1.AuthZRes{Authorized: false}, authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: apiutil.ErrMissingPolicyObj, err: apiutil.ErrMissingPolicyObj,
@@ -220,13 +232,15 @@ func TestAuthorize(t *testing.T) {
{ {
desc: "authorize user with empty permission", desc: "authorize user with empty permission",
token: validToken, token: validToken,
authRequest: &grpcAuthV1.PolicyReq{ authRequest: &grpcAuthV1.AuthZReq{
Subject: id, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: usersType, Subject: id,
Object: authoritiesObj, SubjectType: usersType,
ObjectType: usersType, Object: authoritiesObj,
Relation: memberRelation, ObjectType: usersType,
Permission: "", Relation: memberRelation,
Permission: "",
},
}, },
authResponse: &grpcAuthV1.AuthZRes{Authorized: false}, authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: apiutil.ErrMalformedPolicyPer, err: apiutil.ErrMalformedPolicyPer,
@@ -234,19 +248,24 @@ func TestAuthorize(t *testing.T) {
{ {
desc: "authorize user with valid PAT token", desc: "authorize user with valid PAT token",
token: validPATToken, token: validPATToken,
authRequest: &grpcAuthV1.PolicyReq{ authRequest: &grpcAuthV1.AuthZReq{
Subject: id, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: policies.UserType, Subject: id,
SubjectKind: policies.UsersKind, SubjectType: policies.UserType,
Permission: policies.ViewPermission, SubjectKind: policies.UsersKind,
PatId: id, Permission: policies.ViewPermission,
ObjectType: policies.ClientType, ObjectType: policies.ClientType,
Domain: domainID, Domain: domainID,
Operation: "view", Object: clientID,
Object: clientID, },
UserId: id, PatReq: &grpcAuthV1.PATReq{
EntityId: clientID, PatId: id,
EntityType: auth.ClientsScopeStr, Domain: domainID,
Operation: "view",
UserId: id,
EntityId: clientID,
EntityType: auth.ClientsScopeStr,
},
}, },
authResponse: &grpcAuthV1.AuthZRes{Authorized: true}, authResponse: &grpcAuthV1.AuthZRes{Authorized: true},
err: nil, err: nil,
@@ -254,19 +273,24 @@ func TestAuthorize(t *testing.T) {
{ {
desc: "authorize user with unauthorized PAT token", desc: "authorize user with unauthorized PAT token",
token: inValidToken, token: inValidToken,
authRequest: &grpcAuthV1.PolicyReq{ authRequest: &grpcAuthV1.AuthZReq{
Subject: id, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: policies.UserType, Subject: id,
SubjectKind: policies.UsersKind, SubjectType: policies.UserType,
Permission: policies.ViewPermission, SubjectKind: policies.UsersKind,
PatId: id, Permission: policies.ViewPermission,
ObjectType: policies.ClientType, ObjectType: policies.ClientType,
Domain: domainID, Domain: domainID,
Operation: "view", Object: clientID,
Object: clientID, },
UserId: id, PatReq: &grpcAuthV1.PATReq{
EntityId: clientID, PatId: id,
EntityType: auth.ClientsScopeStr, Domain: domainID,
Operation: "view",
UserId: id,
EntityId: clientID,
EntityType: auth.ClientsScopeStr,
},
}, },
authResponse: &grpcAuthV1.AuthZRes{Authorized: false}, authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: svcerr.ErrAuthorization, err: svcerr.ErrAuthorization,
@@ -274,18 +298,23 @@ func TestAuthorize(t *testing.T) {
{ {
desc: "authorize PAT with missing user id", desc: "authorize PAT with missing user id",
token: validPATToken, token: validPATToken,
authRequest: &grpcAuthV1.PolicyReq{ authRequest: &grpcAuthV1.AuthZReq{
Subject: id, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: policies.UserType, Subject: id,
SubjectKind: policies.UsersKind, SubjectType: policies.UserType,
Permission: policies.ViewPermission, SubjectKind: policies.UsersKind,
PatId: id, Permission: policies.ViewPermission,
ObjectType: policies.ClientType, ObjectType: policies.ClientType,
Domain: domainID, Domain: domainID,
Operation: "view", Object: clientID,
Object: clientID, },
EntityId: clientID, PatReq: &grpcAuthV1.PATReq{
EntityType: auth.ClientsScopeStr, PatId: id,
Domain: domainID,
Operation: "view",
EntityId: clientID,
EntityType: auth.ClientsScopeStr,
},
}, },
authResponse: &grpcAuthV1.AuthZRes{Authorized: false}, authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: apiutil.ErrMissingUserID, err: apiutil.ErrMissingUserID,
@@ -293,18 +322,23 @@ func TestAuthorize(t *testing.T) {
{ {
desc: "authorize PAT with missing entity id", desc: "authorize PAT with missing entity id",
token: validPATToken, token: validPATToken,
authRequest: &grpcAuthV1.PolicyReq{ authRequest: &grpcAuthV1.AuthZReq{
Subject: id, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: policies.UserType, Subject: id,
SubjectKind: policies.UsersKind, SubjectType: policies.UserType,
Permission: policies.ViewPermission, SubjectKind: policies.UsersKind,
PatId: id, Permission: policies.ViewPermission,
ObjectType: policies.ClientType, ObjectType: policies.ClientType,
Domain: domainID, Domain: domainID,
Operation: "view", Object: clientID,
Object: clientID, },
UserId: id, PatReq: &grpcAuthV1.PATReq{
EntityType: auth.ClientsScopeStr, PatId: id,
Domain: domainID,
Operation: "view",
UserId: id,
EntityType: auth.ClientsScopeStr,
},
}, },
authResponse: &grpcAuthV1.AuthZRes{Authorized: false}, authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
err: apiutil.ErrMissingID, err: apiutil.ErrMissingID,
@@ -312,7 +346,7 @@ func TestAuthorize(t *testing.T) {
} }
for _, tc := range cases { for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) { t.Run(tc.desc, func(t *testing.T) {
svcCall := svc.On("Authorize", mock.Anything, mock.Anything).Return(tc.err) svcCall := svc.On("Authorize", mock.Anything, mock.Anything, mock.Anything).Return(tc.err)
ar, err := grpcClient.Authorize(context.Background(), tc.authRequest) ar, err := grpcClient.Authorize(context.Background(), tc.authRequest)
if ar != nil { if ar != nil {
assert.Equal(t, tc.authResponse, ar, fmt.Sprintf("%s: expected %v got %v", tc.desc, tc.authResponse, ar)) assert.Equal(t, tc.authResponse, ar, fmt.Sprintf("%s: expected %v got %v", tc.desc, tc.authResponse, ar))
+29 -17
View File
@@ -45,7 +45,7 @@ func (s *authGrpcServer) Authenticate(ctx context.Context, req *grpcAuthV1.AuthN
return res.(*grpcAuthV1.AuthNRes), nil return res.(*grpcAuthV1.AuthNRes), nil
} }
func (s *authGrpcServer) Authorize(ctx context.Context, req *grpcAuthV1.PolicyReq) (*grpcAuthV1.AuthZRes, error) { func (s *authGrpcServer) Authorize(ctx context.Context, req *grpcAuthV1.AuthZReq) (*grpcAuthV1.AuthZRes, error) {
_, res, err := s.authorize.ServeGRPC(ctx, req) _, res, err := s.authorize.ServeGRPC(ctx, req)
if err != nil { if err != nil {
return nil, grpcapi.EncodeError(err) return nil, grpcapi.EncodeError(err)
@@ -64,26 +64,38 @@ func encodeAuthenticateResponse(_ context.Context, grpcRes any) (any, error) {
} }
func decodeAuthorizeRequest(_ context.Context, grpcReq any) (any, error) { func decodeAuthorizeRequest(_ context.Context, grpcReq any) (any, error) {
req := grpcReq.(*grpcAuthV1.PolicyReq) req := grpcReq.(*grpcAuthV1.AuthZReq)
if req == nil { if req == nil {
return authReq{}, nil return authReq{}, nil
} }
return authReq{ policyReq := req.GetPolicyReq()
Domain: req.GetDomain(), patReq := req.GetPatReq()
SubjectType: req.GetSubjectType(),
SubjectKind: req.GetSubjectKind(), if policyReq == nil {
Subject: req.GetSubject(), return authReq{}, nil
Relation: req.GetRelation(), }
Permission: req.GetPermission(),
ObjectType: req.GetObjectType(), authRequest := authReq{
Object: req.GetObject(), Domain: policyReq.GetDomain(),
UserID: req.GetUserId(), SubjectType: policyReq.GetSubjectType(),
PatID: req.GetPatId(), SubjectKind: policyReq.GetSubjectKind(),
EntityType: req.GetEntityType(), Subject: policyReq.GetSubject(),
Operation: req.GetOperation(), Relation: policyReq.GetRelation(),
EntityID: req.GetEntityId(), Permission: policyReq.GetPermission(),
}, nil ObjectType: policyReq.GetObjectType(),
Object: policyReq.GetObject(),
}
if patReq != nil {
authRequest.UserID = patReq.GetUserId()
authRequest.PatID = patReq.GetPatId()
authRequest.EntityType = patReq.GetEntityType()
authRequest.Operation = patReq.GetOperation()
authRequest.EntityID = patReq.GetEntityId()
}
return authRequest, nil
} }
func encodeAuthorizeResponse(_ context.Context, grpcRes any) (any, error) { func encodeAuthorizeResponse(_ context.Context, grpcRes any) (any, error) {
+14 -2
View File
@@ -110,7 +110,7 @@ func (lm *loggingMiddleware) RetrieveJWKS() (jwks []auth.PublicKeyInfo) {
return lm.svc.RetrieveJWKS() return lm.svc.RetrieveJWKS()
} }
func (lm *loggingMiddleware) Authorize(ctx context.Context, pr policies.Policy) (err error) { func (lm *loggingMiddleware) Authorize(ctx context.Context, pr policies.Policy, patAuthz *auth.PATAuthz) (err error) {
defer func(begin time.Time) { defer func(begin time.Time) {
args := []any{ args := []any{
slog.String("duration", time.Since(begin).String()), slog.String("duration", time.Since(begin).String()),
@@ -125,6 +125,18 @@ func (lm *loggingMiddleware) Authorize(ctx context.Context, pr policies.Policy)
), ),
slog.String("permission", pr.Permission), slog.String("permission", pr.Permission),
} }
if patAuthz != nil {
args = append(args,
slog.Group("pat",
slog.String("pat_id", patAuthz.PatID),
slog.String("user_id", patAuthz.UserID),
slog.String("entity_type", patAuthz.EntityType.String()),
slog.String("entity_id", patAuthz.EntityID),
slog.String("operation", patAuthz.Operation),
slog.String("domain", patAuthz.Domain),
),
)
}
if err != nil { if err != nil {
args = append(args, slog.String("error", err.Error())) args = append(args, slog.String("error", err.Error()))
lm.logger.Warn("Authorize failed", args...) lm.logger.Warn("Authorize failed", args...)
@@ -132,7 +144,7 @@ func (lm *loggingMiddleware) Authorize(ctx context.Context, pr policies.Policy)
} }
lm.logger.Info("Authorize completed successfully", args...) lm.logger.Info("Authorize completed successfully", args...)
}(time.Now()) }(time.Now())
return lm.svc.Authorize(ctx, pr) return lm.svc.Authorize(ctx, pr, patAuthz)
} }
func (lm *loggingMiddleware) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration) (pa auth.PAT, err error) { func (lm *loggingMiddleware) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration) (pa auth.PAT, err error) {
+2 -2
View File
@@ -75,12 +75,12 @@ func (ms *metricsMiddleware) RetrieveJWKS() []auth.PublicKeyInfo {
return ms.svc.RetrieveJWKS() return ms.svc.RetrieveJWKS()
} }
func (ms *metricsMiddleware) Authorize(ctx context.Context, pr policies.Policy) error { func (ms *metricsMiddleware) Authorize(ctx context.Context, pr policies.Policy, patAuthz *auth.PATAuthz) error {
defer func(begin time.Time) { defer func(begin time.Time) {
ms.counter.With("method", "authorize").Add(1) ms.counter.With("method", "authorize").Add(1)
ms.latency.With("method", "authorize").Observe(time.Since(begin).Seconds()) ms.latency.With("method", "authorize").Observe(time.Since(begin).Seconds())
}(time.Now()) }(time.Now())
return ms.svc.Authorize(ctx, pr) return ms.svc.Authorize(ctx, pr, patAuthz)
} }
func (ms *metricsMiddleware) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration) (auth.PAT, error) { func (ms *metricsMiddleware) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration) (auth.PAT, error) {
+17 -4
View File
@@ -65,8 +65,8 @@ func (tm *tracingMiddleware) RetrieveJWKS() []auth.PublicKeyInfo {
return tm.svc.RetrieveJWKS() return tm.svc.RetrieveJWKS()
} }
func (tm *tracingMiddleware) Authorize(ctx context.Context, pr policies.Policy) error { func (tm *tracingMiddleware) Authorize(ctx context.Context, pr policies.Policy, patAuthz *auth.PATAuthz) error {
ctx, span := tm.tracer.Start(ctx, "authorize", trace.WithAttributes( attributes := []attribute.KeyValue{
attribute.String("subject", pr.Subject), attribute.String("subject", pr.Subject),
attribute.String("subject_type", pr.SubjectType), attribute.String("subject_type", pr.SubjectType),
attribute.String("subject_relation", pr.SubjectRelation), attribute.String("subject_relation", pr.SubjectRelation),
@@ -74,10 +74,23 @@ func (tm *tracingMiddleware) Authorize(ctx context.Context, pr policies.Policy)
attribute.String("object_type", pr.ObjectType), attribute.String("object_type", pr.ObjectType),
attribute.String("relation", pr.Relation), attribute.String("relation", pr.Relation),
attribute.String("permission", pr.Permission), attribute.String("permission", pr.Permission),
)) }
if patAuthz != nil {
attributes = append(attributes,
attribute.String("pat_id", patAuthz.PatID),
attribute.String("pat_user_id", patAuthz.UserID),
attribute.String("pat_entity_type", patAuthz.EntityType.String()),
attribute.String("pat_entity_id", patAuthz.EntityID),
attribute.String("pat_operation", patAuthz.Operation),
attribute.String("pat_domain", patAuthz.Domain),
)
}
ctx, span := tm.tracer.Start(ctx, "authorize", trace.WithAttributes(attributes...))
defer span.End() defer span.End()
return tm.svc.Authorize(ctx, pr) return tm.svc.Authorize(ctx, pr, patAuthz)
} }
func (tm *tracingMiddleware) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration) (auth.PAT, error) { func (tm *tracingMiddleware) CreatePAT(ctx context.Context, token, name, description string, duration time.Duration) (auth.PAT, error) {
+15 -8
View File
@@ -11,6 +11,7 @@ package mocks
import ( import (
"context" "context"
"github.com/absmach/supermq/auth"
"github.com/absmach/supermq/pkg/policies" "github.com/absmach/supermq/pkg/policies"
mock "github.com/stretchr/testify/mock" mock "github.com/stretchr/testify/mock"
) )
@@ -43,16 +44,16 @@ func (_m *Authz) EXPECT() *Authz_Expecter {
} }
// Authorize provides a mock function for the type Authz // Authorize provides a mock function for the type Authz
func (_mock *Authz) Authorize(ctx context.Context, pr policies.Policy) error { func (_mock *Authz) Authorize(ctx context.Context, pr policies.Policy, patAuthz *auth.PATAuthz) error {
ret := _mock.Called(ctx, pr) ret := _mock.Called(ctx, pr, patAuthz)
if len(ret) == 0 { if len(ret) == 0 {
panic("no return value specified for Authorize") panic("no return value specified for Authorize")
} }
var r0 error var r0 error
if returnFunc, ok := ret.Get(0).(func(context.Context, policies.Policy) error); ok { if returnFunc, ok := ret.Get(0).(func(context.Context, policies.Policy, *auth.PATAuthz) error); ok {
r0 = returnFunc(ctx, pr) r0 = returnFunc(ctx, pr, patAuthz)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }
@@ -67,11 +68,12 @@ type Authz_Authorize_Call struct {
// Authorize is a helper method to define mock.On call // Authorize is a helper method to define mock.On call
// - ctx context.Context // - ctx context.Context
// - pr policies.Policy // - pr policies.Policy
func (_e *Authz_Expecter) Authorize(ctx interface{}, pr interface{}) *Authz_Authorize_Call { // - patAuthz *auth.PATAuthz
return &Authz_Authorize_Call{Call: _e.mock.On("Authorize", ctx, pr)} func (_e *Authz_Expecter) Authorize(ctx interface{}, pr interface{}, patAuthz interface{}) *Authz_Authorize_Call {
return &Authz_Authorize_Call{Call: _e.mock.On("Authorize", ctx, pr, patAuthz)}
} }
func (_c *Authz_Authorize_Call) Run(run func(ctx context.Context, pr policies.Policy)) *Authz_Authorize_Call { func (_c *Authz_Authorize_Call) Run(run func(ctx context.Context, pr policies.Policy, patAuthz *auth.PATAuthz)) *Authz_Authorize_Call {
_c.Call.Run(func(args mock.Arguments) { _c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context var arg0 context.Context
if args[0] != nil { if args[0] != nil {
@@ -81,9 +83,14 @@ func (_c *Authz_Authorize_Call) Run(run func(ctx context.Context, pr policies.Po
if args[1] != nil { if args[1] != nil {
arg1 = args[1].(policies.Policy) arg1 = args[1].(policies.Policy)
} }
var arg2 *auth.PATAuthz
if args[2] != nil {
arg2 = args[2].(*auth.PATAuthz)
}
run( run(
arg0, arg0,
arg1, arg1,
arg2,
) )
}) })
return _c return _c
@@ -94,7 +101,7 @@ func (_c *Authz_Authorize_Call) Return(err error) *Authz_Authorize_Call {
return _c return _c
} }
func (_c *Authz_Authorize_Call) RunAndReturn(run func(ctx context.Context, pr policies.Policy) error) *Authz_Authorize_Call { func (_c *Authz_Authorize_Call) RunAndReturn(run func(ctx context.Context, pr policies.Policy, patAuthz *auth.PATAuthz) error) *Authz_Authorize_Call {
_c.Call.Return(run) _c.Call.Return(run)
return _c return _c
} }
+14 -8
View File
@@ -114,16 +114,16 @@ func (_c *Service_AddScope_Call) RunAndReturn(run func(ctx context.Context, toke
} }
// Authorize provides a mock function for the type Service // Authorize provides a mock function for the type Service
func (_mock *Service) Authorize(ctx context.Context, pr policies.Policy) error { func (_mock *Service) Authorize(ctx context.Context, pr policies.Policy, patAuthz *auth.PATAuthz) error {
ret := _mock.Called(ctx, pr) ret := _mock.Called(ctx, pr, patAuthz)
if len(ret) == 0 { if len(ret) == 0 {
panic("no return value specified for Authorize") panic("no return value specified for Authorize")
} }
var r0 error var r0 error
if returnFunc, ok := ret.Get(0).(func(context.Context, policies.Policy) error); ok { if returnFunc, ok := ret.Get(0).(func(context.Context, policies.Policy, *auth.PATAuthz) error); ok {
r0 = returnFunc(ctx, pr) r0 = returnFunc(ctx, pr, patAuthz)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }
@@ -138,11 +138,12 @@ type Service_Authorize_Call struct {
// Authorize is a helper method to define mock.On call // Authorize is a helper method to define mock.On call
// - ctx context.Context // - ctx context.Context
// - pr policies.Policy // - pr policies.Policy
func (_e *Service_Expecter) Authorize(ctx interface{}, pr interface{}) *Service_Authorize_Call { // - patAuthz *auth.PATAuthz
return &Service_Authorize_Call{Call: _e.mock.On("Authorize", ctx, pr)} func (_e *Service_Expecter) Authorize(ctx interface{}, pr interface{}, patAuthz interface{}) *Service_Authorize_Call {
return &Service_Authorize_Call{Call: _e.mock.On("Authorize", ctx, pr, patAuthz)}
} }
func (_c *Service_Authorize_Call) Run(run func(ctx context.Context, pr policies.Policy)) *Service_Authorize_Call { func (_c *Service_Authorize_Call) Run(run func(ctx context.Context, pr policies.Policy, patAuthz *auth.PATAuthz)) *Service_Authorize_Call {
_c.Call.Run(func(args mock.Arguments) { _c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context var arg0 context.Context
if args[0] != nil { if args[0] != nil {
@@ -152,9 +153,14 @@ func (_c *Service_Authorize_Call) Run(run func(ctx context.Context, pr policies.
if args[1] != nil { if args[1] != nil {
arg1 = args[1].(policies.Policy) arg1 = args[1].(policies.Policy)
} }
var arg2 *auth.PATAuthz
if args[2] != nil {
arg2 = args[2].(*auth.PATAuthz)
}
run( run(
arg0, arg0,
arg1, arg1,
arg2,
) )
}) })
return _c return _c
@@ -165,7 +171,7 @@ func (_c *Service_Authorize_Call) Return(err error) *Service_Authorize_Call {
return _c return _c
} }
func (_c *Service_Authorize_Call) RunAndReturn(run func(ctx context.Context, pr policies.Policy) error) *Service_Authorize_Call { func (_c *Service_Authorize_Call) RunAndReturn(run func(ctx context.Context, pr policies.Policy, patAuthz *auth.PATAuthz) error) *Service_Authorize_Call {
_c.Call.Return(run) _c.Call.Return(run)
return _c return _c
} }
+16
View File
@@ -69,6 +69,7 @@ const (
DashboardType DashboardType
MessagesType MessagesType
DomainsType DomainsType
UsersType
) )
const ( const (
@@ -78,6 +79,7 @@ const (
DashboardsStr = "dashboards" DashboardsStr = "dashboards"
MessagesStr = "messages" MessagesStr = "messages"
DomainsStr = "domains" DomainsStr = "domains"
UsersStr = "users"
) )
func (et EntityType) String() string { func (et EntityType) String() string {
@@ -94,6 +96,8 @@ func (et EntityType) String() string {
return MessagesStr return MessagesStr
case DomainsType: case DomainsType:
return DomainsStr return DomainsStr
case UsersType:
return UsersStr
default: default:
return fmt.Sprintf("unknown domain entity type %d", et) return fmt.Sprintf("unknown domain entity type %d", et)
} }
@@ -113,6 +117,8 @@ func ParseEntityType(et string) (EntityType, error) {
return MessagesType, nil return MessagesType, nil
case DomainsStr: case DomainsStr:
return DomainsType, nil return DomainsType, nil
case UsersStr:
return UsersType, nil
default: default:
return 0, fmt.Errorf("unknown domain entity type %s", et) return 0, fmt.Errorf("unknown domain entity type %s", et)
} }
@@ -280,6 +286,16 @@ func (s *Scope) Validate() error {
return nil return nil
} }
// PATAuthz represents the PAT authorization request fields.
type PATAuthz struct {
PatID string
UserID string
EntityType EntityType
EntityID string
Operation string
Domain string
}
// PAT represents Personal Access Token. // PAT represents Personal Access Token.
type PAT struct { type PAT struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
+7 -11
View File
@@ -56,7 +56,7 @@ type Authz interface {
// `object`. Authorize returns a non-nil error if the subject has // `object`. Authorize returns a non-nil error if the subject has
// no relation on the object (which simply means the operation is // no relation on the object (which simply means the operation is
// denied). // denied).
Authorize(ctx context.Context, pr policies.Policy) error Authorize(ctx context.Context, pr policies.Policy, patAuthz *PATAuthz) error
} }
// Authn specifies an API that must be fulfilled by the domain service // Authn specifies an API that must be fulfilled by the domain service
@@ -205,13 +205,9 @@ func (svc service) RetrieveJWKS() []PublicKeyInfo {
return keys return keys
} }
func (svc service) Authorize(ctx context.Context, pr policies.Policy) error { func (svc service) Authorize(ctx context.Context, pr policies.Policy, patAuthz *PATAuthz) error {
if pr.PatID != "" { if patAuthz != nil {
entityType, err := ParseEntityType(pr.EntityType) if err := svc.AuthorizePAT(ctx, patAuthz.UserID, patAuthz.PatID, patAuthz.EntityType, patAuthz.Domain, patAuthz.Operation, patAuthz.EntityID); err != nil {
if err != nil {
return err
}
if err := svc.AuthorizePAT(ctx, pr.UserID, pr.PatID, entityType, pr.Domain, pr.Operation, pr.EntityID); err != nil {
return err return err
} }
} }
@@ -336,7 +332,7 @@ func (svc service) checkUserRole(ctx context.Context, key Key) (err error) {
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
}); err != nil { }, nil); err != nil {
return errRoleAuth return errRoleAuth
} }
return nil return nil
@@ -347,7 +343,7 @@ func (svc service) checkUserRole(ctx context.Context, key Key) (err error) {
Permission: policies.MembershipPermission, Permission: policies.MembershipPermission,
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
}); err != nil { }, nil); err != nil {
return errRoleAuth return errRoleAuth
} }
return nil return nil
@@ -364,7 +360,7 @@ func (svc service) getUserRole(ctx context.Context, userID string) (role Role) {
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
}); err == nil { }, nil); err == nil {
rl = AdminRole rl = AdminRole
} }
+105 -72
View File
@@ -648,19 +648,20 @@ func TestAuthorize(t *testing.T) {
cases := []struct { cases := []struct {
desc string desc string
policyReq policies.Policy policyReq policies.Policy
patAuthz *auth.PATAuthz
checkDomainPolicyReq policies.Policy
checkPolicyReq policies.Policy checkPolicyReq policies.Policy
patEntityType auth.EntityType patErr error
patScopeErr error
expectPATCheck bool
expectCheckPolicy bool
parseReq string parseReq string
parseRes auth.Key parseRes auth.Key
parseErr error parseErr error
callBackErr error callBackErr error
checkPolicyErr error checkPolicyErr error
checkDomainPolicyErr error checkDomainPolicyErr error
authorizePATErr error
err error err error
}{ }{
{ {
desc: "authorize a user key successfully", desc: "authorize a user key successfully",
policyReq: policies.Policy{ policyReq: policies.Policy{
@@ -677,41 +678,51 @@ func TestAuthorize(t *testing.T) {
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
}, },
expectCheckPolicy: true, checkDomainPolicyReq: policies.Policy{
err: nil, Subject: userID,
SubjectType: policies.UserType,
Object: validID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
},
err: nil,
}, },
{ {
desc: "authorize with PAT scope successfully", desc: "authorize with PAT successfully",
policyReq: policies.Policy{ policyReq: policies.Policy{
SubjectType: policies.UserType, SubjectType: policies.UserType,
SubjectKind: policies.UsersKind, SubjectKind: policies.UsersKind,
Object: policies.SuperMQObject, Subject: userID,
ObjectType: policies.PlatformType, Object: validID,
Permission: policies.AdminPermission, ObjectType: policies.ClientType,
PatID: validID, Permission: policies.ViewPermission,
UserID: userID,
EntityType: auth.ChannelsScopeStr,
Domain: domainID, Domain: domainID,
Operation: auth.OpListChannels, },
EntityID: auth.AnyIDs, patAuthz: &auth.PATAuthz{
PatID: validID,
UserID: userID,
EntityType: auth.ClientsType,
EntityID: validID,
Operation: "read",
Domain: domainID,
},
checkDomainPolicyReq: policies.Policy{
Subject: userID,
SubjectType: policies.UserType,
Object: domainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}, },
checkPolicyReq: policies.Policy{ checkPolicyReq: policies.Policy{
SubjectType: policies.UserType, SubjectType: policies.UserType,
SubjectKind: policies.UsersKind, SubjectKind: policies.UsersKind,
Object: policies.SuperMQObject, Subject: userID,
ObjectType: policies.PlatformType, Object: validID,
Permission: policies.AdminPermission, ObjectType: policies.ClientType,
PatID: validID, Permission: policies.ViewPermission,
UserID: userID,
EntityType: auth.ChannelsScopeStr,
Domain: domainID, Domain: domainID,
Operation: auth.OpListChannels,
EntityID: auth.AnyIDs,
}, },
patEntityType: auth.ChannelsType, err: nil,
expectPATCheck: true,
expectCheckPolicy: true,
err: nil,
}, },
{ {
desc: "authorize with PAT scope check failure", desc: "authorize with PAT scope check failure",
@@ -721,12 +732,14 @@ func TestAuthorize(t *testing.T) {
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
PatID: validID, },
UserID: userID, patAuthz: &auth.PATAuthz{
EntityType: auth.ChannelsScopeStr, PatID: validID,
Domain: domainID, UserID: userID,
Operation: auth.OpListChannels, EntityType: auth.ChannelsType,
EntityID: auth.AnyIDs, Domain: domainID,
Operation: auth.OpListChannels,
EntityID: auth.AnyIDs,
}, },
checkPolicyReq: policies.Policy{ checkPolicyReq: policies.Policy{
SubjectType: policies.UserType, SubjectType: policies.UserType,
@@ -734,17 +747,9 @@ func TestAuthorize(t *testing.T) {
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
PatID: validID,
UserID: userID,
EntityType: auth.ChannelsScopeStr,
Domain: domainID,
Operation: auth.OpListChannels,
EntityID: auth.AnyIDs,
}, },
patEntityType: auth.ChannelsType, patErr: svcerr.ErrAuthorization,
patScopeErr: repoerr.ErrNotFound, err: svcerr.ErrAuthorization,
expectPATCheck: true,
err: svcerr.ErrAuthorization,
}, },
{ {
desc: "authorize with invalid PAT entity type", desc: "authorize with invalid PAT entity type",
@@ -754,12 +759,14 @@ func TestAuthorize(t *testing.T) {
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
PatID: validID, },
UserID: userID, patAuthz: &auth.PATAuthz{
EntityType: "invalid", PatID: validID,
Domain: domainID, UserID: userID,
Operation: auth.OpListChannels, EntityType: auth.EntityType(100),
EntityID: auth.AnyIDs, Domain: domainID,
Operation: auth.OpListChannels,
EntityID: auth.AnyIDs,
}, },
checkPolicyReq: policies.Policy{ checkPolicyReq: policies.Policy{
SubjectType: policies.UserType, SubjectType: policies.UserType,
@@ -767,32 +774,55 @@ func TestAuthorize(t *testing.T) {
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
PatID: validID,
UserID: userID,
EntityType: "invalid",
Domain: domainID,
Operation: auth.OpListChannels,
EntityID: auth.AnyIDs,
}, },
err: errors.New("unknown domain entity type invalid"), patErr: errors.New("unknown domain entity type invalid"),
err: errors.New("unknown domain entity type invalid"),
}, },
{ {
desc: "authorize invalid platform object", desc: "authorize with PAT but PAT authorization fails",
policyReq: policies.Policy{ policyReq: policies.Policy{
SubjectType: policies.UserType, SubjectType: policies.UserType,
SubjectKind: policies.UsersKind, SubjectKind: policies.UsersKind,
Object: "invalid", Subject: userID,
ObjectType: policies.PlatformType, Object: validID,
Permission: policies.AdminPermission, ObjectType: policies.ClientType,
Permission: policies.ViewPermission,
}, },
checkPolicyReq: policies.Policy{ patAuthz: &auth.PATAuthz{
PatID: validID,
UserID: userID,
EntityType: auth.ClientsType,
EntityID: validID,
Operation: "read",
Domain: domainID,
},
checkPolicyReq: policies.Policy{},
authorizePATErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
},
{
desc: "authorize with PAT - PAT authorization fails but policy check not reached",
policyReq: policies.Policy{
SubjectType: policies.UserType, SubjectType: policies.UserType,
SubjectKind: policies.UsersKind, SubjectKind: policies.UsersKind,
Object: "invalid", Subject: userID,
ObjectType: policies.PlatformType, Object: validID,
Permission: policies.AdminPermission, ObjectType: policies.ClientType,
Permission: policies.ViewPermission,
}, },
err: svcerr.ErrMalformedEntity, patAuthz: &auth.PATAuthz{
PatID: validID,
UserID: userID,
EntityType: auth.ClientsType,
EntityID: validID,
Operation: "write",
Domain: domainID,
},
checkPolicyReq: policies.Policy{},
authorizePATErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
}, },
{ {
desc: "authorize with policy check error", desc: "authorize with policy check error",
@@ -810,23 +840,26 @@ func TestAuthorize(t *testing.T) {
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
}, },
checkPolicyErr: repoerr.ErrNotFound, checkPolicyErr: repoerr.ErrNotFound,
expectCheckPolicy: true, err: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
}, },
} }
for _, tc := range cases { for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) { t.Run(tc.desc, func(t *testing.T) {
var policyCall *mock.Call var policyCall *mock.Call
if tc.expectCheckPolicy { if tc.checkPolicyReq != (policies.Policy{}) {
policyCall = pEvaluator.On("CheckPolicy", mock.Anything, tc.checkPolicyReq).Return(tc.checkPolicyErr) policyCall = pEvaluator.On("CheckPolicy", mock.Anything, tc.checkPolicyReq).Return(tc.checkPolicyErr)
} }
var patCall *mock.Call var patCall *mock.Call
if tc.expectPATCheck { if tc.patAuthz != nil {
patCall = patsrepo.On("CheckScope", mock.Anything, tc.policyReq.UserID, tc.policyReq.PatID, tc.patEntityType, tc.policyReq.Domain, tc.policyReq.Operation, tc.policyReq.EntityID).Return(tc.patScopeErr) patErr := tc.patErr
if patErr == nil {
patErr = tc.authorizePATErr
}
patCall = patsrepo.On("CheckScope", mock.Anything, tc.patAuthz.UserID, tc.patAuthz.PatID, tc.patAuthz.EntityType, tc.patAuthz.Domain, tc.patAuthz.Operation, tc.patAuthz.EntityID).Return(patErr)
} }
repoCall := krepo.On("Remove", mock.Anything, mock.Anything, mock.Anything).Return(nil) repoCall := krepo.On("Remove", mock.Anything, mock.Anything, mock.Anything).Return(nil)
err := svc.Authorize(context.Background(), tc.policyReq) err := svc.Authorize(context.Background(), tc.policyReq, tc.patAuthz)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s expected %s got %s\n", tc.desc, tc.err, err)) assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s expected %s got %s\n", tc.desc, tc.err, err))
if policyCall != nil { if policyCall != nil {
policyCall.Unset() policyCall.Unset()
+18 -9
View File
@@ -326,9 +326,8 @@ func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, sessio
} }
func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.Session, entityType string, op permissions.Operation, req smqauthz.PolicyReq) error { func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.Session, entityType string, op permissions.Operation, req smqauthz.PolicyReq) error {
req.UserID = session.UserID
req.PatID = session.PatID
req.Domain = session.DomainID req.Domain = session.DomainID
perm, err := am.entitiesOps.GetPermission(entityType, op) perm, err := am.entitiesOps.GetPermission(entityType, op)
if err != nil { if err != nil {
return err return err
@@ -336,14 +335,24 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.
req.Permission = perm.String() req.Permission = perm.String()
req.EntityID = req.Object var pat *smqauthz.PATReq
req.EntityType = auth.ChannelsType.String() if session.PatID != "" {
req.Operation = am.entitiesOps.OperationName(entityType, op) entityID := req.Object
if op == operations.OpListUserChannels || op == dOperations.OpCreateDomainChannels || op == dOperations.OpListDomainChannels { opName := am.entitiesOps.OperationName(entityType, op)
req.EntityID = auth.AnyIDs if op == operations.OpListUserChannels || op == dOperations.OpCreateDomainChannels || op == dOperations.OpListDomainChannels {
entityID = auth.AnyIDs
}
pat = &smqauthz.PATReq{
UserID: session.UserID,
PatID: session.PatID,
EntityID: entityID,
EntityType: auth.ChannelsType.String(),
Operation: opName,
Domain: session.DomainID,
}
} }
if err := am.authz.Authorize(ctx, req); err != nil { if err := am.authz.Authorize(ctx, req, pat); err != nil {
return err return err
} }
@@ -360,7 +369,7 @@ func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, session
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
}); err != nil { }, nil); err != nil {
return err return err
} }
return nil return nil
+17 -9
View File
@@ -257,8 +257,6 @@ func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, sessio
} }
func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.Session, entityType string, op permissions.Operation, req smqauthz.PolicyReq) error { func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.Session, entityType string, op permissions.Operation, req smqauthz.PolicyReq) error {
req.UserID = session.UserID
req.PatID = session.PatID
req.Domain = session.DomainID req.Domain = session.DomainID
perm, err := am.entitiesOps.GetPermission(entityType, op) perm, err := am.entitiesOps.GetPermission(entityType, op)
@@ -268,14 +266,24 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.
req.Permission = perm.String() req.Permission = perm.String()
req.EntityID = req.Object var pat *smqauthz.PATReq
req.EntityType = auth.ClientsType.String() if session.PatID != "" {
req.Operation = am.entitiesOps.OperationName(entityType, op) entityID := req.Object
if op == operations.OpListUserClients || op == dOperations.OpCreateDomainClients || op == dOperations.OpListDomainClients { opName := am.entitiesOps.OperationName(entityType, op)
req.EntityID = auth.AnyIDs if op == operations.OpListUserClients || op == dOperations.OpCreateDomainClients || op == dOperations.OpListDomainClients {
entityID = auth.AnyIDs
}
pat = &smqauthz.PATReq{
UserID: session.UserID,
PatID: session.PatID,
EntityID: entityID,
EntityType: auth.ClientsType.String(),
Operation: opName,
Domain: session.DomainID,
}
} }
if err := am.authz.Authorize(ctx, req); err != nil { if err := am.authz.Authorize(ctx, req, pat); err != nil {
return err return err
} }
@@ -292,7 +300,7 @@ func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, session
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
}); err != nil { }, nil); err != nil {
return err return err
} }
return nil return nil
+1 -1
View File
@@ -412,7 +412,7 @@ func createAdminPolicy(ctx context.Context, userID string, authz smqauthz.Author
Permission: policies.AdministratorRelation, Permission: policies.AdministratorRelation,
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
}); err != nil { }, nil); err != nil {
err := policyService.AddPolicy(ctx, policies.Policy{ err := policyService.AddPolicy(ctx, policies.Policy{
SubjectType: policies.UserType, SubjectType: policies.UserType,
Subject: userID, Subject: userID,
+62 -11
View File
@@ -6,6 +6,7 @@ package middleware
import ( import (
"context" "context"
"github.com/absmach/supermq/auth"
"github.com/absmach/supermq/domains" "github.com/absmach/supermq/domains"
"github.com/absmach/supermq/domains/operations" "github.com/absmach/supermq/domains/operations"
"github.com/absmach/supermq/pkg/authn" "github.com/absmach/supermq/pkg/authn"
@@ -61,7 +62,7 @@ func (am *authorizationMiddleware) RetrieveDomain(ctx context.Context, session a
return am.svc.RetrieveDomain(ctx, session, id, withRoles) return am.svc.RetrieveDomain(ctx, session, id, withRoles)
} }
if err := am.authorize(ctx, policies.DomainType, operations.OpRetrieveDomain, authz.PolicyReq{ if err := am.authorize(ctx, session, policies.DomainType, operations.OpRetrieveDomain, authz.PolicyReq{
Subject: session.DomainUserID, Subject: session.DomainUserID,
SubjectType: policies.UserType, SubjectType: policies.UserType,
SubjectKind: policies.UsersKind, SubjectKind: policies.UsersKind,
@@ -75,7 +76,7 @@ func (am *authorizationMiddleware) RetrieveDomain(ctx context.Context, session a
} }
func (am *authorizationMiddleware) UpdateDomain(ctx context.Context, session authn.Session, id string, d domains.DomainReq) (domains.Domain, error) { func (am *authorizationMiddleware) UpdateDomain(ctx context.Context, session authn.Session, id string, d domains.DomainReq) (domains.Domain, error) {
if err := am.authorize(ctx, policies.DomainType, operations.OpUpdateDomain, authz.PolicyReq{ if err := am.authorize(ctx, session, policies.DomainType, operations.OpUpdateDomain, authz.PolicyReq{
Subject: session.DomainUserID, Subject: session.DomainUserID,
SubjectType: policies.UserType, SubjectType: policies.UserType,
SubjectKind: policies.UsersKind, SubjectKind: policies.UsersKind,
@@ -89,7 +90,7 @@ func (am *authorizationMiddleware) UpdateDomain(ctx context.Context, session aut
} }
func (am *authorizationMiddleware) EnableDomain(ctx context.Context, session authn.Session, id string) (domains.Domain, error) { func (am *authorizationMiddleware) EnableDomain(ctx context.Context, session authn.Session, id string) (domains.Domain, error) {
if err := am.authorize(ctx, policies.DomainType, operations.OpEnableDomain, authz.PolicyReq{ if err := am.authorize(ctx, session, policies.DomainType, operations.OpEnableDomain, authz.PolicyReq{
Subject: session.DomainUserID, Subject: session.DomainUserID,
SubjectType: policies.UserType, SubjectType: policies.UserType,
SubjectKind: policies.UsersKind, SubjectKind: policies.UsersKind,
@@ -103,7 +104,7 @@ func (am *authorizationMiddleware) EnableDomain(ctx context.Context, session aut
} }
func (am *authorizationMiddleware) DisableDomain(ctx context.Context, session authn.Session, id string) (domains.Domain, error) { func (am *authorizationMiddleware) DisableDomain(ctx context.Context, session authn.Session, id string) (domains.Domain, error) {
if err := am.authorize(ctx, policies.DomainType, operations.OpDisableDomain, authz.PolicyReq{ if err := am.authorize(ctx, session, policies.DomainType, operations.OpDisableDomain, authz.PolicyReq{
Subject: session.DomainUserID, Subject: session.DomainUserID,
SubjectType: policies.UserType, SubjectType: policies.UserType,
SubjectKind: policies.UsersKind, SubjectKind: policies.UsersKind,
@@ -125,7 +126,7 @@ func (am *authorizationMiddleware) FreezeDomain(ctx context.Context, session aut
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
}); err != nil { }, nil); err != nil {
return domains.Domain{}, err return domains.Domain{}, err
} }
@@ -141,7 +142,7 @@ func (am *authorizationMiddleware) ListDomains(ctx context.Context, session auth
} }
func (am *authorizationMiddleware) SendInvitation(ctx context.Context, session authn.Session, invitation domains.Invitation) (domains.Invitation, error) { func (am *authorizationMiddleware) SendInvitation(ctx context.Context, session authn.Session, invitation domains.Invitation) (domains.Invitation, error) {
if err := am.authorize(ctx, policies.DomainType, operations.OpSendDomainInvitation, authz.PolicyReq{ if err := am.authorize(ctx, session, policies.DomainType, operations.OpSendDomainInvitation, authz.PolicyReq{
Subject: session.DomainUserID, Subject: session.DomainUserID,
SubjectType: policies.UserType, SubjectType: policies.UserType,
SubjectKind: policies.UsersKind, SubjectKind: policies.UsersKind,
@@ -151,6 +152,10 @@ func (am *authorizationMiddleware) SendInvitation(ctx context.Context, session a
return domains.Invitation{}, err return domains.Invitation{}, err
} }
if err := am.checkAdmin(ctx, session); err != nil {
return domains.Invitation{}, err
}
return am.svc.SendInvitation(ctx, session, invitation) return am.svc.SendInvitation(ctx, session, invitation)
} }
@@ -159,7 +164,7 @@ func (am *authorizationMiddleware) ListInvitations(ctx context.Context, session
} }
func (am *authorizationMiddleware) ListDomainInvitations(ctx context.Context, session authn.Session, page domains.InvitationPageMeta) (invs domains.InvitationPage, err error) { func (am *authorizationMiddleware) ListDomainInvitations(ctx context.Context, session authn.Session, page domains.InvitationPageMeta) (invs domains.InvitationPage, err error) {
if err := am.authorize(ctx, policies.DomainType, operations.OpListDomainInvitations, authz.PolicyReq{ if err := am.authorize(ctx, session, policies.DomainType, operations.OpListDomainInvitations, authz.PolicyReq{
Subject: session.DomainUserID, Subject: session.DomainUserID,
SubjectType: policies.UserType, SubjectType: policies.UserType,
SubjectKind: policies.UsersKind, SubjectKind: policies.UsersKind,
@@ -181,7 +186,7 @@ func (am *authorizationMiddleware) RejectInvitation(ctx context.Context, session
} }
func (am *authorizationMiddleware) DeleteInvitation(ctx context.Context, session authn.Session, inviteeUserID, domainID string) (err error) { func (am *authorizationMiddleware) DeleteInvitation(ctx context.Context, session authn.Session, inviteeUserID, domainID string) (err error) {
if err := am.authorize(ctx, policies.DomainType, operations.OpDeleteDomainInvitation, authz.PolicyReq{ if err := am.authorize(ctx, session, policies.DomainType, operations.OpDeleteDomainInvitation, authz.PolicyReq{
Subject: session.DomainUserID, Subject: session.DomainUserID,
SubjectType: policies.UserType, SubjectType: policies.UserType,
SubjectKind: policies.UsersKind, SubjectKind: policies.UsersKind,
@@ -194,7 +199,9 @@ func (am *authorizationMiddleware) DeleteInvitation(ctx context.Context, session
return am.svc.DeleteInvitation(ctx, session, inviteeUserID, domainID) return am.svc.DeleteInvitation(ctx, session, inviteeUserID, domainID)
} }
func (am *authorizationMiddleware) authorize(ctx context.Context, entityType string, op permissions.Operation, authReq authz.PolicyReq) error { func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.Session, entityType string, op permissions.Operation, authReq authz.PolicyReq) error {
authReq.Domain = session.DomainID
perm, err := am.entitiesOps.GetPermission(entityType, op) perm, err := am.entitiesOps.GetPermission(entityType, op)
if err != nil { if err != nil {
return err return err
@@ -202,13 +209,57 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, entityType str
authReq.Permission = perm.String() authReq.Permission = perm.String()
if err := am.authz.Authorize(ctx, authReq); err != nil { var pat *smqauthz.PATReq
if session.PatID != "" {
entityID := authReq.Object
opName := am.entitiesOps.OperationName(entityType, op)
pat = &smqauthz.PATReq{
UserID: session.UserID,
PatID: session.PatID,
EntityID: entityID,
EntityType: auth.DomainsType.String(),
Operation: opName,
Domain: session.DomainID,
}
}
if err := am.authz.Authorize(ctx, authReq, pat); err != nil {
return err return err
} }
return nil return nil
} }
// checkAdmin checks if the given user is a domain or platform administrator.
func (am *authorizationMiddleware) checkAdmin(ctx context.Context, session authn.Session) error {
req := smqauthz.PolicyReq{
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Permission: policies.AdminPermission,
ObjectType: policies.DomainType,
Object: session.DomainID,
}
if err := am.authz.Authorize(ctx, req, nil); err == nil {
return nil
}
req = smqauthz.PolicyReq{
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.UserID,
Permission: policies.AdminPermission,
ObjectType: policies.PlatformType,
Object: policies.SuperMQObject,
}
if err := am.authz.Authorize(ctx, req, nil); err == nil {
return nil
}
return svcerr.ErrAuthorization
}
func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, session authn.Session) error { func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, session authn.Session) error {
if session.Role != authn.AdminRole { if session.Role != authn.AdminRole {
return svcerr.ErrSuperAdminAction return svcerr.ErrSuperAdminAction
@@ -219,7 +270,7 @@ func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, session
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
}); err != nil { }, nil); err != nil {
return err return err
} }
return nil return nil
+17 -9
View File
@@ -376,15 +376,13 @@ func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, session
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
}); err != nil { }, nil); err != nil {
return err return err
} }
return nil return nil
} }
func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.Session, entityType string, op permissions.Operation, pr smqauthz.PolicyReq) error { func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.Session, entityType string, op permissions.Operation, pr smqauthz.PolicyReq) error {
pr.UserID = session.UserID
pr.PatID = session.PatID
pr.Domain = session.DomainID pr.Domain = session.DomainID
perm, err := am.entitiesOps.GetPermission(entityType, op) perm, err := am.entitiesOps.GetPermission(entityType, op)
@@ -393,14 +391,24 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.
} }
pr.Permission = perm.String() pr.Permission = perm.String()
pr.EntityID = pr.Object var pat *smqauthz.PATReq
pr.EntityType = auth.GroupsType.String() if session.PatID != "" {
pr.Operation = am.entitiesOps.OperationName(entityType, op) entityID := pr.Object
if op == dOperations.OpListDomainGroups || op == dOperations.OpCreateDomainGroups { opName := am.entitiesOps.OperationName(entityType, op)
pr.EntityID = auth.AnyIDs if op == dOperations.OpListDomainGroups || op == dOperations.OpCreateDomainGroups {
entityID = auth.AnyIDs
}
pat = &smqauthz.PATReq{
UserID: session.UserID,
PatID: session.PatID,
EntityID: entityID,
EntityType: auth.GroupsType.String(),
Operation: opName,
Domain: session.DomainID,
}
} }
if err := am.authz.Authorize(ctx, pr); err != nil { if err := am.authz.Authorize(ctx, pr, pat); err != nil {
return err return err
} }
return nil return nil
+15 -6
View File
@@ -9,7 +9,7 @@ option go_package = "github.com/absmach/supermq/api/grpc/auth/v1";
// AuthService is a service that provides authentication // AuthService is a service that provides authentication
// and authorization functionalities for SuperMQ services. // and authorization functionalities for SuperMQ services.
service AuthService { service AuthService {
rpc Authorize(PolicyReq) returns (AuthZRes) {} rpc Authorize(AuthZReq) returns (AuthZRes) {}
rpc Authenticate(AuthNReq) returns (AuthNRes) {} rpc Authenticate(AuthNReq) returns (AuthNRes) {}
} }
@@ -35,11 +35,20 @@ message PolicyReq {
string permission = 7; string permission = 7;
string object = 8; string object = 8;
string object_type = 9; string object_type = 9;
string pat_id = 10; }
string operation = 11;
string user_id = 12; message PATReq {
string entity_id = 13; string pat_id = 1;
string entity_type = 14; string domain = 2;
string operation = 3;
string user_id = 4;
string entity_id = 5;
string entity_type = 6;
}
message AuthZReq {
PolicyReq policy_req = 1;
optional PATReq pat_req = 2;
} }
message AuthZRes { message AuthZRes {
+2 -2
View File
@@ -58,7 +58,7 @@ func (am *authorizationMiddleware) RetrieveAll(ctx context.Context, session smqa
ObjectType: objectType, ObjectType: objectType,
Object: object, Object: object,
} }
if err := am.authz.Authorize(ctx, req); err != nil { if err := am.authz.Authorize(ctx, req, nil); err != nil {
return journal.JournalsPage{}, err return journal.JournalsPage{}, err
} }
@@ -76,7 +76,7 @@ func (am *authorizationMiddleware) RetrieveClientTelemetry(ctx context.Context,
Object: clientID, Object: clientID,
} }
if err := am.authz.Authorize(ctx, req); err != nil { if err := am.authz.Authorize(ctx, req, nil); err != nil {
return journal.ClientTelemetry{}, err return journal.ClientTelemetry{}, err
} }
+48 -34
View File
@@ -46,7 +46,7 @@ func NewAuthorization(ctx context.Context, cfg grpcclient.Config, domainsAuthz p
}, client, nil }, client, nil
} }
func (a authorization) Authorize(ctx context.Context, pr authz.PolicyReq) error { func (a authorization) Authorize(ctx context.Context, pr authz.PolicyReq, pat *authz.PATReq) error {
if pr.SubjectType == policies.UserType && (pr.ObjectType == policies.GroupType || pr.ObjectType == policies.ClientType || pr.ObjectType == policies.DomainType) { if pr.SubjectType == policies.UserType && (pr.ObjectType == policies.GroupType || pr.ObjectType == policies.ClientType || pr.ObjectType == policies.DomainType) {
domainID := pr.Domain domainID := pr.Domain
if domainID == "" { if domainID == "" {
@@ -60,21 +60,29 @@ func (a authorization) Authorize(ctx context.Context, pr authz.PolicyReq) error
} }
} }
req := grpcAuthV1.PolicyReq{ req := grpcAuthV1.AuthZReq{
Domain: pr.Domain, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: pr.SubjectType, Domain: pr.Domain,
SubjectKind: pr.SubjectKind, SubjectType: pr.SubjectType,
SubjectRelation: pr.SubjectRelation, SubjectKind: pr.SubjectKind,
Subject: pr.Subject, SubjectRelation: pr.SubjectRelation,
Relation: pr.Relation, Subject: pr.Subject,
Permission: pr.Permission, Relation: pr.Relation,
Object: pr.Object, Permission: pr.Permission,
ObjectType: pr.ObjectType, Object: pr.Object,
PatId: pr.PatID, ObjectType: pr.ObjectType,
Operation: pr.Operation, },
UserId: pr.UserID, }
EntityId: pr.EntityID,
EntityType: pr.EntityType, if pat != nil {
req.PatReq = &grpcAuthV1.PATReq{
PatId: pat.PatID,
Domain: pat.Domain,
Operation: pat.Operation,
UserId: pat.UserID,
EntityId: pat.EntityID,
EntityType: pat.EntityType,
}
} }
res, err := a.authSvcClient.Authorize(ctx, &req) res, err := a.authSvcClient.Authorize(ctx, &req)
@@ -95,32 +103,38 @@ func (a authorization) checkDomain(ctx context.Context, subjectType, subject, do
switch status { switch status {
case domains.FreezeStatus: case domains.FreezeStatus:
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.PolicyReq{ _, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.AuthZReq{
Subject: subject, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: subjectType, Subject: subject,
Permission: policies.AdminPermission, SubjectType: subjectType,
Object: policies.SuperMQObject, Permission: policies.AdminPermission,
ObjectType: policies.PlatformType, Object: policies.SuperMQObject,
ObjectType: policies.PlatformType,
},
}) })
return err return err
case domains.DisabledStatus: case domains.DisabledStatus:
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.PolicyReq{ _, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.AuthZReq{
Subject: subject, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: subjectType, Subject: subject,
Permission: policies.AdminPermission, SubjectType: subjectType,
Object: domainID, Permission: policies.AdminPermission,
ObjectType: policies.DomainType, Object: domainID,
ObjectType: policies.DomainType,
},
}) })
return err return err
case domains.EnabledStatus: case domains.EnabledStatus:
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.PolicyReq{ _, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.AuthZReq{
Subject: subject, PolicyReq: &grpcAuthV1.PolicyReq{
SubjectType: subjectType, Subject: subject,
Permission: policies.MembershipPermission, SubjectType: subjectType,
Object: domainID, Permission: policies.MembershipPermission,
ObjectType: policies.DomainType, Object: domainID,
ObjectType: policies.DomainType,
},
}) })
return err return err
+21 -8
View File
@@ -42,17 +42,30 @@ type PolicyReq struct {
// Permission contains the permission. Supported permissions are admin, delete, edit, share, view, // Permission contains the permission. Supported permissions are admin, delete, edit, share, view,
// membership, create, admin_only, edit_only, view_only, membership_only, ext_admin, ext_edit, ext_view. // membership, create, admin_only, edit_only, view_only, membership_only, ext_admin, ext_edit, ext_view.
Permission string `json:"permission,omitempty"` Permission string `json:"permission,omitempty"`
}
// PAT authorization fields // PATReq represents a Personal Access Token authorization request.
UserID string `json:"user_id,omitempty"` type PATReq struct {
PatID string `json:"pat_id,omitempty"` // PatID contains the personal access token ID.
EntityType string `json:"entity_type,omitempty"` PatID string `json:"pat_id"`
DomainID string `json:"domain_id,omitempty"`
Operation string `json:"operation,omitempty"` // Domain contains the domain ID.
EntityID string `json:"entity_id,omitempty"` Domain string `json:"domain"`
// Operation contains the operation type for PAT authorization.
Operation string `json:"operation"`
// UserID contains the user ID for PAT authorization.
UserID string `json:"user_id"`
// EntityID contains the entity ID for PAT authorization.
EntityID string `json:"entity_id"`
// EntityType contains the entity type for PAT authorization.
EntityType string `json:"entity_type"`
} }
// Authz is supermq authorization library. // Authz is supermq authorization library.
type Authorization interface { type Authorization interface {
Authorize(ctx context.Context, pr PolicyReq) error Authorize(ctx context.Context, pr PolicyReq, pat *PATReq) error
} }
+14 -8
View File
@@ -43,16 +43,16 @@ func (_m *Authorization) EXPECT() *Authorization_Expecter {
} }
// Authorize provides a mock function for the type Authorization // Authorize provides a mock function for the type Authorization
func (_mock *Authorization) Authorize(ctx context.Context, pr authz.PolicyReq) error { func (_mock *Authorization) Authorize(ctx context.Context, pr authz.PolicyReq, pat *authz.PATReq) error {
ret := _mock.Called(ctx, pr) ret := _mock.Called(ctx, pr, pat)
if len(ret) == 0 { if len(ret) == 0 {
panic("no return value specified for Authorize") panic("no return value specified for Authorize")
} }
var r0 error var r0 error
if returnFunc, ok := ret.Get(0).(func(context.Context, authz.PolicyReq) error); ok { if returnFunc, ok := ret.Get(0).(func(context.Context, authz.PolicyReq, *authz.PATReq) error); ok {
r0 = returnFunc(ctx, pr) r0 = returnFunc(ctx, pr, pat)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }
@@ -67,11 +67,12 @@ type Authorization_Authorize_Call struct {
// Authorize is a helper method to define mock.On call // Authorize is a helper method to define mock.On call
// - ctx context.Context // - ctx context.Context
// - pr authz.PolicyReq // - pr authz.PolicyReq
func (_e *Authorization_Expecter) Authorize(ctx interface{}, pr interface{}) *Authorization_Authorize_Call { // - pat *authz.PATReq
return &Authorization_Authorize_Call{Call: _e.mock.On("Authorize", ctx, pr)} func (_e *Authorization_Expecter) Authorize(ctx interface{}, pr interface{}, pat interface{}) *Authorization_Authorize_Call {
return &Authorization_Authorize_Call{Call: _e.mock.On("Authorize", ctx, pr, pat)}
} }
func (_c *Authorization_Authorize_Call) Run(run func(ctx context.Context, pr authz.PolicyReq)) *Authorization_Authorize_Call { func (_c *Authorization_Authorize_Call) Run(run func(ctx context.Context, pr authz.PolicyReq, pat *authz.PATReq)) *Authorization_Authorize_Call {
_c.Call.Run(func(args mock.Arguments) { _c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context var arg0 context.Context
if args[0] != nil { if args[0] != nil {
@@ -81,9 +82,14 @@ func (_c *Authorization_Authorize_Call) Run(run func(ctx context.Context, pr aut
if args[1] != nil { if args[1] != nil {
arg1 = args[1].(authz.PolicyReq) arg1 = args[1].(authz.PolicyReq)
} }
var arg2 *authz.PATReq
if args[2] != nil {
arg2 = args[2].(*authz.PATReq)
}
run( run(
arg0, arg0,
arg1, arg1,
arg2,
) )
}) })
return _c return _c
@@ -94,7 +100,7 @@ func (_c *Authorization_Authorize_Call) Return(err error) *Authorization_Authori
return _c return _c
} }
func (_c *Authorization_Authorize_Call) RunAndReturn(run func(ctx context.Context, pr authz.PolicyReq) error) *Authorization_Authorize_Call { func (_c *Authorization_Authorize_Call) RunAndReturn(run func(ctx context.Context, pr authz.PolicyReq, pat *authz.PATReq) error) *Authorization_Authorize_Call {
_c.Call.Return(run) _c.Call.Return(run)
return _c return _c
} }
-13
View File
@@ -49,19 +49,6 @@ type Policy struct {
// Permission contains the permission. Supported permissions are admin, delete, edit, share, view, // Permission contains the permission. Supported permissions are admin, delete, edit, share, view,
// membership, create, admin_only, edit_only, view_only, membership_only, ext_admin, ext_edit, ext_view. // membership, create, admin_only, edit_only, view_only, membership_only, ext_admin, ext_edit, ext_view.
Permission string `json:"permission,omitempty"` Permission string `json:"permission,omitempty"`
// PAT authorization fields
// PatID contains the personal access token ID.
PatID string `json:"pat_id,omitempty"`
// Operation contains the operation type for PAT authorization.
Operation string `json:"operation,omitempty"`
// UserID contains the user ID for PAT authorization.
UserID string `json:"user_id,omitempty"`
// EntityType contains the entity type for PAT authorization.
EntityType string `json:"entity_type,omitempty"`
// EntityID contains the entity ID for PAT authorization.
EntityID string `json:"entity_id,omitempty"`
} }
func (pr Policy) String() string { func (pr Policy) String() string {
@@ -5,6 +5,7 @@ package middleware
import ( import (
"context" "context"
"fmt"
"github.com/absmach/supermq/auth" "github.com/absmach/supermq/auth"
"github.com/absmach/supermq/pkg/authn" "github.com/absmach/supermq/pkg/authn"
@@ -293,8 +294,6 @@ func (ram RoleManagerAuthorizationMiddleware) RoleRemoveMembers(ctx context.Cont
} }
func (ram RoleManagerAuthorizationMiddleware) authorize(ctx context.Context, session authn.Session, 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.UserID = session.UserID
pr.PatID = session.PatID
pr.Domain = session.DomainID pr.Domain = session.DomainID
perm, err := ram.ops.GetPermission(op) perm, err := ram.ops.GetPermission(op)
@@ -304,19 +303,31 @@ func (ram RoleManagerAuthorizationMiddleware) authorize(ctx context.Context, ses
pr.Permission = perm.String() pr.Permission = perm.String()
pr.EntityID = pr.Object var pat *smqauthz.PATReq
opName := ram.ops.OperationName(op) if session.PatID != "" {
switch pr.ObjectType { opName := ram.ops.OperationName(op)
case policies.GroupType: var patEntityType string
pr.EntityType = auth.GroupsType.String() switch pr.ObjectType {
case policies.ClientType: case policies.GroupType:
pr.EntityType = auth.ClientsType.String() patEntityType = auth.GroupsType.String()
case policies.ChannelType: case policies.ClientType:
pr.EntityType = auth.ChannelsType.String() patEntityType = auth.ClientsType.String()
case policies.ChannelType:
patEntityType = auth.ChannelsType.String()
default:
return errors.Wrap(errors.ErrAuthorization, fmt.Errorf("unsupported entity type for PAT: %s", pr.ObjectType))
}
pat = &smqauthz.PATReq{
UserID: session.UserID,
PatID: session.PatID,
EntityID: pr.Object,
EntityType: patEntityType,
Operation: auth.RoleOperationPrefix + opName,
Domain: session.DomainID,
}
} }
pr.Operation = auth.RoleOperationPrefix + opName
if err := ram.authz.Authorize(ctx, pr); err != nil { if err := ram.authz.Authorize(ctx, pr, pat); err != nil {
return errors.Wrap(errors.ErrAuthorization, err) return errors.Wrap(errors.ErrAuthorization, err)
} }
@@ -338,7 +349,7 @@ func (ram RoleManagerAuthorizationMiddleware) validateMembers(ctx context.Contex
SubjectKind: policies.UsersKind, SubjectKind: policies.UsersKind,
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
}); err != nil { }, nil); err != nil {
return errors.Wrap(errors.ErrMissingMember, err) return errors.Wrap(errors.ErrMissingMember, err)
} }
} }
@@ -353,7 +364,7 @@ func (ram RoleManagerAuthorizationMiddleware) validateMembers(ctx context.Contex
SubjectKind: policies.UsersKind, SubjectKind: policies.UsersKind,
Object: session.DomainID, Object: session.DomainID,
ObjectType: policies.DomainType, ObjectType: policies.DomainType,
}); err != nil { }, nil); err != nil {
return errors.Wrap(errors.ErrMissingDomainMember, err) return errors.Wrap(errors.ErrMissingDomainMember, err)
} }
} }
+15 -2
View File
@@ -7,6 +7,7 @@ import (
"context" "context"
grpcTokenV1 "github.com/absmach/supermq/api/grpc/token/v1" grpcTokenV1 "github.com/absmach/supermq/api/grpc/token/v1"
"github.com/absmach/supermq/auth"
"github.com/absmach/supermq/pkg/authn" "github.com/absmach/supermq/pkg/authn"
smqauthz "github.com/absmach/supermq/pkg/authz" smqauthz "github.com/absmach/supermq/pkg/authz"
svcerr "github.com/absmach/supermq/pkg/errors/service" svcerr "github.com/absmach/supermq/pkg/errors/service"
@@ -190,7 +191,7 @@ func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, session
Permission: policies.AdminPermission, Permission: policies.AdminPermission,
ObjectType: policies.PlatformType, ObjectType: policies.PlatformType,
Object: policies.SuperMQObject, Object: policies.SuperMQObject,
}); err != nil { }, nil); err != nil {
return err return err
} }
return nil return nil
@@ -207,7 +208,19 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.
Object: obj, Object: obj,
} }
if err := am.authz.Authorize(ctx, req); err != nil { var pat *smqauthz.PATReq
if session.PatID != "" {
pat = &smqauthz.PATReq{
UserID: session.UserID,
PatID: session.PatID,
EntityID: subj,
EntityType: auth.UsersType.String(),
Operation: perm,
Domain: domain,
}
}
if err := am.authz.Authorize(ctx, req, pat); err != nil {
return err return err
} }
return nil return nil