mirror of
https://github.com/absmach/supermq.git
synced 2026-06-23 06:50:18 +00:00
NOISSUE - Seperate PAT from policy (#3330)
Continuous Delivery / lint-and-build (push) Has been cancelled
Continuous Delivery / Build and Push Docker Images (push) Has been cancelled
Deploy GitHub Pages / swagger-ui (push) Has been cancelled
CI Pipeline / Check Certs (push) Has been cancelled
CI Pipeline / Lint Proto (push) Has been cancelled
CI Pipeline / lint-and-build (push) Has been cancelled
CI Pipeline / Detect Changes (push) Has been cancelled
CI Pipeline / Upload Coverage (push) Has been cancelled
CI Pipeline / Test ${{ matrix.module }} (push) Has been cancelled
Property Based Tests / api-test (push) Has been cancelled
Continuous Delivery / lint-and-build (push) Has been cancelled
Continuous Delivery / Build and Push Docker Images (push) Has been cancelled
Deploy GitHub Pages / swagger-ui (push) Has been cancelled
CI Pipeline / Check Certs (push) Has been cancelled
CI Pipeline / Lint Proto (push) Has been cancelled
CI Pipeline / lint-and-build (push) Has been cancelled
CI Pipeline / Detect Changes (push) Has been cancelled
CI Pipeline / Upload Coverage (push) Has been cancelled
CI Pipeline / Test ${{ matrix.module }} (push) Has been cancelled
Property Based Tests / api-test (push) Has been cancelled
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:
+145
-37
@@ -147,11 +147,6 @@ type PolicyReq struct {
|
||||
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"`
|
||||
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
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -249,41 +244,142 @@ func (x *PolicyReq) GetObjectType() string {
|
||||
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 {
|
||||
return x.PatId
|
||||
}
|
||||
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 {
|
||||
return x.Operation
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PolicyReq) GetUserId() string {
|
||||
func (x *PATReq) GetUserId() string {
|
||||
if x != nil {
|
||||
return x.UserId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PolicyReq) GetEntityId() string {
|
||||
func (x *PATReq) GetEntityId() string {
|
||||
if x != nil {
|
||||
return x.EntityId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PolicyReq) GetEntityType() string {
|
||||
func (x *PATReq) GetEntityType() string {
|
||||
if x != nil {
|
||||
return x.EntityType
|
||||
}
|
||||
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 {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Authorized bool `protobuf:"varint,1,opt,name=authorized,proto3" json:"authorized,omitempty"`
|
||||
@@ -294,7 +390,7 @@ type AuthZRes struct {
|
||||
|
||||
func (x *AuthZRes) Reset() {
|
||||
*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.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -306,7 +402,7 @@ func (x *AuthZRes) String() string {
|
||||
func (*AuthZRes) ProtoMessage() {}
|
||||
|
||||
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 {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -319,7 +415,7 @@ func (x *AuthZRes) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AuthZRes.ProtoReflect.Descriptor instead.
|
||||
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 {
|
||||
@@ -347,7 +443,7 @@ const file_auth_v1_auth_proto_rawDesc = "" +
|
||||
"\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" +
|
||||
"\auser_id\x18\x02 \x01(\tR\x06userId\x12\x1b\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" +
|
||||
"\x06domain\x18\x01 \x01(\tR\x06domain\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" +
|
||||
"\x06object\x18\b \x01(\tR\x06object\x12\x1f\n" +
|
||||
"\vobject_type\x18\t \x01(\tR\n" +
|
||||
"objectType\x12\x15\n" +
|
||||
"\x06pat_id\x18\n" +
|
||||
" \x01(\tR\x05patId\x12\x1c\n" +
|
||||
"\toperation\x18\v \x01(\tR\toperation\x12\x17\n" +
|
||||
"\auser_id\x18\f \x01(\tR\x06userId\x12\x1b\n" +
|
||||
"\tentity_id\x18\r \x01(\tR\bentityId\x12\x1f\n" +
|
||||
"\ventity_type\x18\x0e \x01(\tR\n" +
|
||||
"entityType\":\n" +
|
||||
"objectType\"\xac\x01\n" +
|
||||
"\x06PATReq\x12\x15\n" +
|
||||
"\x06pat_id\x18\x01 \x01(\tR\x05patId\x12\x16\n" +
|
||||
"\x06domain\x18\x02 \x01(\tR\x06domain\x12\x1c\n" +
|
||||
"\toperation\x18\x03 \x01(\tR\toperation\x12\x17\n" +
|
||||
"\auser_id\x18\x04 \x01(\tR\x06userId\x12\x1b\n" +
|
||||
"\tentity_id\x18\x05 \x01(\tR\bentityId\x12\x1f\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" +
|
||||
"\n" +
|
||||
"authorized\x18\x01 \x01(\bR\n" +
|
||||
"authorized\x12\x0e\n" +
|
||||
"\x02id\x18\x02 \x01(\tR\x02id2{\n" +
|
||||
"\vAuthService\x124\n" +
|
||||
"\tAuthorize\x12\x12.auth.v1.PolicyReq\x1a\x11.auth.v1.AuthZRes\"\x00\x126\n" +
|
||||
"\x02id\x18\x02 \x01(\tR\x02id2z\n" +
|
||||
"\vAuthService\x123\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"
|
||||
|
||||
var (
|
||||
@@ -389,23 +492,27 @@ func file_auth_v1_auth_proto_rawDescGZIP() []byte {
|
||||
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{
|
||||
(*AuthNReq)(nil), // 0: auth.v1.AuthNReq
|
||||
(*AuthNRes)(nil), // 1: auth.v1.AuthNRes
|
||||
(*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{
|
||||
2, // 0: auth.v1.AuthService.Authorize:input_type -> auth.v1.PolicyReq
|
||||
0, // 1: auth.v1.AuthService.Authenticate:input_type -> auth.v1.AuthNReq
|
||||
3, // 2: auth.v1.AuthService.Authorize:output_type -> auth.v1.AuthZRes
|
||||
1, // 3: auth.v1.AuthService.Authenticate:output_type -> auth.v1.AuthNRes
|
||||
2, // [2:4] is the sub-list for method output_type
|
||||
0, // [0:2] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
2, // 0: auth.v1.AuthZReq.policy_req:type_name -> auth.v1.PolicyReq
|
||||
3, // 1: auth.v1.AuthZReq.pat_req:type_name -> auth.v1.PATReq
|
||||
4, // 2: auth.v1.AuthService.Authorize:input_type -> auth.v1.AuthZReq
|
||||
0, // 3: auth.v1.AuthService.Authenticate:input_type -> auth.v1.AuthNReq
|
||||
5, // 4: auth.v1.AuthService.Authorize:output_type -> auth.v1.AuthZRes
|
||||
1, // 5: auth.v1.AuthService.Authenticate:output_type -> auth.v1.AuthNRes
|
||||
4, // [4:6] is the sub-list for method output_type
|
||||
2, // [2:4] is the sub-list for method input_type
|
||||
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() }
|
||||
@@ -413,13 +520,14 @@ func file_auth_v1_auth_proto_init() {
|
||||
if File_auth_v1_auth_proto != nil {
|
||||
return
|
||||
}
|
||||
file_auth_v1_auth_proto_msgTypes[4].OneofWrappers = []any{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_auth_v1_auth_proto_rawDesc), len(file_auth_v1_auth_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -33,7 +33,7 @@ const (
|
||||
// AuthService is a service that provides authentication
|
||||
// and authorization functionalities for SuperMQ services.
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func NewAuthServiceClient(cc grpc.ClientConnInterface) AuthServiceClient {
|
||||
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...)
|
||||
out := new(AuthZRes)
|
||||
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
|
||||
// and authorization functionalities for SuperMQ services.
|
||||
type AuthServiceServer interface {
|
||||
Authorize(context.Context, *PolicyReq) (*AuthZRes, error)
|
||||
Authorize(context.Context, *AuthZReq) (*AuthZRes, error)
|
||||
Authenticate(context.Context, *AuthNReq) (*AuthNRes, error)
|
||||
mustEmbedUnimplementedAuthServiceServer()
|
||||
}
|
||||
@@ -84,7 +84,7 @@ type AuthServiceServer interface {
|
||||
// pointer dereference when methods are called.
|
||||
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")
|
||||
}
|
||||
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) {
|
||||
in := new(PolicyReq)
|
||||
in := new(AuthZReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -124,7 +124,7 @@ func _AuthService_Authorize_Handler(srv interface{}, ctx context.Context, dec fu
|
||||
FullMethod: AuthService_Authorize_FullMethodName,
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
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)
|
||||
defer cancel()
|
||||
|
||||
var authReqData authReq
|
||||
|
||||
if req != nil {
|
||||
authReqData = authReq{
|
||||
Domain: req.GetDomain(),
|
||||
SubjectType: req.GetSubjectType(),
|
||||
Subject: req.GetSubject(),
|
||||
SubjectKind: req.GetSubjectKind(),
|
||||
Relation: req.GetRelation(),
|
||||
Permission: req.GetPermission(),
|
||||
ObjectType: req.GetObjectType(),
|
||||
Object: req.GetObject(),
|
||||
UserID: req.GetUserId(),
|
||||
PatID: req.GetPatId(),
|
||||
EntityType: req.GetEntityType(),
|
||||
Operation: req.GetOperation(),
|
||||
EntityID: req.GetEntityId(),
|
||||
policyReq := req.GetPolicyReq()
|
||||
patReq := req.GetPatReq()
|
||||
|
||||
if policyReq != nil {
|
||||
authReqData = authReq{
|
||||
Domain: policyReq.GetDomain(),
|
||||
SubjectType: policyReq.GetSubjectType(),
|
||||
Subject: policyReq.GetSubject(),
|
||||
SubjectKind: policyReq.GetSubjectKind(),
|
||||
Relation: policyReq.GetRelation(),
|
||||
Permission: policyReq.GetPermission(),
|
||||
ObjectType: policyReq.GetObjectType(),
|
||||
Object: policyReq.GetObject(),
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
req := grpcReq.(authReq)
|
||||
|
||||
return &grpcAuthV1.PolicyReq{
|
||||
Domain: req.Domain,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
SubjectKind: req.SubjectKind,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
UserId: req.UserID,
|
||||
PatId: req.PatID,
|
||||
EntityType: req.EntityType,
|
||||
Operation: req.Operation,
|
||||
EntityId: req.EntityID,
|
||||
}, nil
|
||||
authZReq := &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Domain: req.Domain,
|
||||
SubjectType: req.SubjectType,
|
||||
Subject: req.Subject,
|
||||
SubjectKind: req.SubjectKind,
|
||||
Relation: req.Relation,
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
},
|
||||
}
|
||||
|
||||
if req.PatID != "" {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -35,6 +35,22 @@ func authorizeEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
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{
|
||||
Domain: req.Domain,
|
||||
SubjectType: req.SubjectType,
|
||||
@@ -44,12 +60,7 @@ func authorizeEndpoint(svc auth.Service) endpoint.Endpoint {
|
||||
Permission: req.Permission,
|
||||
ObjectType: req.ObjectType,
|
||||
Object: req.Object,
|
||||
PatID: req.PatID,
|
||||
Operation: req.Operation,
|
||||
UserID: req.UserID,
|
||||
EntityType: req.EntityType,
|
||||
EntityID: req.EntityID,
|
||||
})
|
||||
}, pat)
|
||||
if err != nil {
|
||||
return authorizeRes{authorized: false}, err
|
||||
}
|
||||
|
||||
+135
-101
@@ -129,20 +129,22 @@ func TestAuthorize(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
authRequest *grpcAuthV1.PolicyReq
|
||||
authRequest *grpcAuthV1.AuthZReq
|
||||
authResponse *grpcAuthV1.AuthZRes
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "authorize user with authorized token",
|
||||
token: validToken,
|
||||
authRequest: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: usersType,
|
||||
Object: authoritiesObj,
|
||||
ObjectType: usersType,
|
||||
Relation: memberRelation,
|
||||
Permission: adminPermission,
|
||||
authRequest: &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: usersType,
|
||||
Object: authoritiesObj,
|
||||
ObjectType: usersType,
|
||||
Relation: memberRelation,
|
||||
Permission: adminPermission,
|
||||
},
|
||||
},
|
||||
authResponse: &grpcAuthV1.AuthZRes{Authorized: true},
|
||||
err: nil,
|
||||
@@ -150,13 +152,15 @@ func TestAuthorize(t *testing.T) {
|
||||
{
|
||||
desc: "authorize user with unauthorized token",
|
||||
token: inValidToken,
|
||||
authRequest: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: usersType,
|
||||
Object: authoritiesObj,
|
||||
ObjectType: usersType,
|
||||
Relation: memberRelation,
|
||||
Permission: adminPermission,
|
||||
authRequest: &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: usersType,
|
||||
Object: authoritiesObj,
|
||||
ObjectType: usersType,
|
||||
Relation: memberRelation,
|
||||
Permission: adminPermission,
|
||||
},
|
||||
},
|
||||
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
|
||||
err: svcerr.ErrAuthorization,
|
||||
@@ -164,13 +168,15 @@ func TestAuthorize(t *testing.T) {
|
||||
{
|
||||
desc: "authorize user with empty subject",
|
||||
token: validToken,
|
||||
authRequest: &grpcAuthV1.PolicyReq{
|
||||
Subject: "",
|
||||
SubjectType: usersType,
|
||||
Object: authoritiesObj,
|
||||
ObjectType: usersType,
|
||||
Relation: memberRelation,
|
||||
Permission: adminPermission,
|
||||
authRequest: &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: "",
|
||||
SubjectType: usersType,
|
||||
Object: authoritiesObj,
|
||||
ObjectType: usersType,
|
||||
Relation: memberRelation,
|
||||
Permission: adminPermission,
|
||||
},
|
||||
},
|
||||
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
|
||||
err: apiutil.ErrMissingPolicySub,
|
||||
@@ -178,13 +184,15 @@ func TestAuthorize(t *testing.T) {
|
||||
{
|
||||
desc: "authorize user with empty subject type",
|
||||
token: validToken,
|
||||
authRequest: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: "",
|
||||
Object: authoritiesObj,
|
||||
ObjectType: usersType,
|
||||
Relation: memberRelation,
|
||||
Permission: adminPermission,
|
||||
authRequest: &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: "",
|
||||
Object: authoritiesObj,
|
||||
ObjectType: usersType,
|
||||
Relation: memberRelation,
|
||||
Permission: adminPermission,
|
||||
},
|
||||
},
|
||||
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
|
||||
err: apiutil.ErrMissingPolicySub,
|
||||
@@ -192,13 +200,15 @@ func TestAuthorize(t *testing.T) {
|
||||
{
|
||||
desc: "authorize user with empty object",
|
||||
token: validToken,
|
||||
authRequest: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: usersType,
|
||||
Object: "",
|
||||
ObjectType: usersType,
|
||||
Relation: memberRelation,
|
||||
Permission: adminPermission,
|
||||
authRequest: &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: usersType,
|
||||
Object: "",
|
||||
ObjectType: usersType,
|
||||
Relation: memberRelation,
|
||||
Permission: adminPermission,
|
||||
},
|
||||
},
|
||||
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
|
||||
err: apiutil.ErrMissingPolicyObj,
|
||||
@@ -206,13 +216,15 @@ func TestAuthorize(t *testing.T) {
|
||||
{
|
||||
desc: "authorize user with empty object type",
|
||||
token: validToken,
|
||||
authRequest: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: usersType,
|
||||
Object: authoritiesObj,
|
||||
ObjectType: "",
|
||||
Relation: memberRelation,
|
||||
Permission: adminPermission,
|
||||
authRequest: &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: usersType,
|
||||
Object: authoritiesObj,
|
||||
ObjectType: "",
|
||||
Relation: memberRelation,
|
||||
Permission: adminPermission,
|
||||
},
|
||||
},
|
||||
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
|
||||
err: apiutil.ErrMissingPolicyObj,
|
||||
@@ -220,13 +232,15 @@ func TestAuthorize(t *testing.T) {
|
||||
{
|
||||
desc: "authorize user with empty permission",
|
||||
token: validToken,
|
||||
authRequest: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: usersType,
|
||||
Object: authoritiesObj,
|
||||
ObjectType: usersType,
|
||||
Relation: memberRelation,
|
||||
Permission: "",
|
||||
authRequest: &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: usersType,
|
||||
Object: authoritiesObj,
|
||||
ObjectType: usersType,
|
||||
Relation: memberRelation,
|
||||
Permission: "",
|
||||
},
|
||||
},
|
||||
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
|
||||
err: apiutil.ErrMalformedPolicyPer,
|
||||
@@ -234,19 +248,24 @@ func TestAuthorize(t *testing.T) {
|
||||
{
|
||||
desc: "authorize user with valid PAT token",
|
||||
token: validPATToken,
|
||||
authRequest: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Permission: policies.ViewPermission,
|
||||
PatId: id,
|
||||
ObjectType: policies.ClientType,
|
||||
Domain: domainID,
|
||||
Operation: "view",
|
||||
Object: clientID,
|
||||
UserId: id,
|
||||
EntityId: clientID,
|
||||
EntityType: auth.ClientsScopeStr,
|
||||
authRequest: &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Permission: policies.ViewPermission,
|
||||
ObjectType: policies.ClientType,
|
||||
Domain: domainID,
|
||||
Object: clientID,
|
||||
},
|
||||
PatReq: &grpcAuthV1.PATReq{
|
||||
PatId: id,
|
||||
Domain: domainID,
|
||||
Operation: "view",
|
||||
UserId: id,
|
||||
EntityId: clientID,
|
||||
EntityType: auth.ClientsScopeStr,
|
||||
},
|
||||
},
|
||||
authResponse: &grpcAuthV1.AuthZRes{Authorized: true},
|
||||
err: nil,
|
||||
@@ -254,19 +273,24 @@ func TestAuthorize(t *testing.T) {
|
||||
{
|
||||
desc: "authorize user with unauthorized PAT token",
|
||||
token: inValidToken,
|
||||
authRequest: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Permission: policies.ViewPermission,
|
||||
PatId: id,
|
||||
ObjectType: policies.ClientType,
|
||||
Domain: domainID,
|
||||
Operation: "view",
|
||||
Object: clientID,
|
||||
UserId: id,
|
||||
EntityId: clientID,
|
||||
EntityType: auth.ClientsScopeStr,
|
||||
authRequest: &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Permission: policies.ViewPermission,
|
||||
ObjectType: policies.ClientType,
|
||||
Domain: domainID,
|
||||
Object: clientID,
|
||||
},
|
||||
PatReq: &grpcAuthV1.PATReq{
|
||||
PatId: id,
|
||||
Domain: domainID,
|
||||
Operation: "view",
|
||||
UserId: id,
|
||||
EntityId: clientID,
|
||||
EntityType: auth.ClientsScopeStr,
|
||||
},
|
||||
},
|
||||
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
|
||||
err: svcerr.ErrAuthorization,
|
||||
@@ -274,18 +298,23 @@ func TestAuthorize(t *testing.T) {
|
||||
{
|
||||
desc: "authorize PAT with missing user id",
|
||||
token: validPATToken,
|
||||
authRequest: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Permission: policies.ViewPermission,
|
||||
PatId: id,
|
||||
ObjectType: policies.ClientType,
|
||||
Domain: domainID,
|
||||
Operation: "view",
|
||||
Object: clientID,
|
||||
EntityId: clientID,
|
||||
EntityType: auth.ClientsScopeStr,
|
||||
authRequest: &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Permission: policies.ViewPermission,
|
||||
ObjectType: policies.ClientType,
|
||||
Domain: domainID,
|
||||
Object: clientID,
|
||||
},
|
||||
PatReq: &grpcAuthV1.PATReq{
|
||||
PatId: id,
|
||||
Domain: domainID,
|
||||
Operation: "view",
|
||||
EntityId: clientID,
|
||||
EntityType: auth.ClientsScopeStr,
|
||||
},
|
||||
},
|
||||
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
|
||||
err: apiutil.ErrMissingUserID,
|
||||
@@ -293,18 +322,23 @@ func TestAuthorize(t *testing.T) {
|
||||
{
|
||||
desc: "authorize PAT with missing entity id",
|
||||
token: validPATToken,
|
||||
authRequest: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Permission: policies.ViewPermission,
|
||||
PatId: id,
|
||||
ObjectType: policies.ClientType,
|
||||
Domain: domainID,
|
||||
Operation: "view",
|
||||
Object: clientID,
|
||||
UserId: id,
|
||||
EntityType: auth.ClientsScopeStr,
|
||||
authRequest: &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: id,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Permission: policies.ViewPermission,
|
||||
ObjectType: policies.ClientType,
|
||||
Domain: domainID,
|
||||
Object: clientID,
|
||||
},
|
||||
PatReq: &grpcAuthV1.PATReq{
|
||||
PatId: id,
|
||||
Domain: domainID,
|
||||
Operation: "view",
|
||||
UserId: id,
|
||||
EntityType: auth.ClientsScopeStr,
|
||||
},
|
||||
},
|
||||
authResponse: &grpcAuthV1.AuthZRes{Authorized: false},
|
||||
err: apiutil.ErrMissingID,
|
||||
@@ -312,7 +346,7 @@ func TestAuthorize(t *testing.T) {
|
||||
}
|
||||
for _, tc := range cases {
|
||||
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)
|
||||
if ar != nil {
|
||||
assert.Equal(t, tc.authResponse, ar, fmt.Sprintf("%s: expected %v got %v", tc.desc, tc.authResponse, ar))
|
||||
|
||||
@@ -45,7 +45,7 @@ func (s *authGrpcServer) Authenticate(ctx context.Context, req *grpcAuthV1.AuthN
|
||||
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)
|
||||
if err != nil {
|
||||
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) {
|
||||
req := grpcReq.(*grpcAuthV1.PolicyReq)
|
||||
req := grpcReq.(*grpcAuthV1.AuthZReq)
|
||||
if req == nil {
|
||||
return authReq{}, nil
|
||||
}
|
||||
|
||||
return authReq{
|
||||
Domain: req.GetDomain(),
|
||||
SubjectType: req.GetSubjectType(),
|
||||
SubjectKind: req.GetSubjectKind(),
|
||||
Subject: req.GetSubject(),
|
||||
Relation: req.GetRelation(),
|
||||
Permission: req.GetPermission(),
|
||||
ObjectType: req.GetObjectType(),
|
||||
Object: req.GetObject(),
|
||||
UserID: req.GetUserId(),
|
||||
PatID: req.GetPatId(),
|
||||
EntityType: req.GetEntityType(),
|
||||
Operation: req.GetOperation(),
|
||||
EntityID: req.GetEntityId(),
|
||||
}, nil
|
||||
policyReq := req.GetPolicyReq()
|
||||
patReq := req.GetPatReq()
|
||||
|
||||
if policyReq == nil {
|
||||
return authReq{}, nil
|
||||
}
|
||||
|
||||
authRequest := authReq{
|
||||
Domain: policyReq.GetDomain(),
|
||||
SubjectType: policyReq.GetSubjectType(),
|
||||
SubjectKind: policyReq.GetSubjectKind(),
|
||||
Subject: policyReq.GetSubject(),
|
||||
Relation: policyReq.GetRelation(),
|
||||
Permission: policyReq.GetPermission(),
|
||||
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) {
|
||||
|
||||
@@ -110,7 +110,7 @@ func (lm *loggingMiddleware) RetrieveJWKS() (jwks []auth.PublicKeyInfo) {
|
||||
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) {
|
||||
args := []any{
|
||||
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),
|
||||
}
|
||||
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 {
|
||||
args = append(args, slog.String("error", err.Error()))
|
||||
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...)
|
||||
}(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) {
|
||||
|
||||
@@ -75,12 +75,12 @@ func (ms *metricsMiddleware) RetrieveJWKS() []auth.PublicKeyInfo {
|
||||
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) {
|
||||
ms.counter.With("method", "authorize").Add(1)
|
||||
ms.latency.With("method", "authorize").Observe(time.Since(begin).Seconds())
|
||||
}(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) {
|
||||
|
||||
@@ -65,8 +65,8 @@ func (tm *tracingMiddleware) RetrieveJWKS() []auth.PublicKeyInfo {
|
||||
return tm.svc.RetrieveJWKS()
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) Authorize(ctx context.Context, pr policies.Policy) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "authorize", trace.WithAttributes(
|
||||
func (tm *tracingMiddleware) Authorize(ctx context.Context, pr policies.Policy, patAuthz *auth.PATAuthz) error {
|
||||
attributes := []attribute.KeyValue{
|
||||
attribute.String("subject", pr.Subject),
|
||||
attribute.String("subject_type", pr.SubjectType),
|
||||
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("relation", pr.Relation),
|
||||
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()
|
||||
|
||||
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) {
|
||||
|
||||
+15
-8
@@ -11,6 +11,7 @@ package mocks
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/absmach/supermq/auth"
|
||||
"github.com/absmach/supermq/pkg/policies"
|
||||
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
|
||||
func (_mock *Authz) Authorize(ctx context.Context, pr policies.Policy) error {
|
||||
ret := _mock.Called(ctx, pr)
|
||||
func (_mock *Authz) Authorize(ctx context.Context, pr policies.Policy, patAuthz *auth.PATAuthz) error {
|
||||
ret := _mock.Called(ctx, pr, patAuthz)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Authorize")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, policies.Policy) error); ok {
|
||||
r0 = returnFunc(ctx, pr)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, policies.Policy, *auth.PATAuthz) error); ok {
|
||||
r0 = returnFunc(ctx, pr, patAuthz)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
@@ -67,11 +68,12 @@ type Authz_Authorize_Call struct {
|
||||
// Authorize is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - pr policies.Policy
|
||||
func (_e *Authz_Expecter) Authorize(ctx interface{}, pr interface{}) *Authz_Authorize_Call {
|
||||
return &Authz_Authorize_Call{Call: _e.mock.On("Authorize", ctx, pr)}
|
||||
// - patAuthz *auth.PATAuthz
|
||||
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) {
|
||||
var arg0 context.Context
|
||||
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 {
|
||||
arg1 = args[1].(policies.Policy)
|
||||
}
|
||||
var arg2 *auth.PATAuthz
|
||||
if args[2] != nil {
|
||||
arg2 = args[2].(*auth.PATAuthz)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
@@ -94,7 +101,7 @@ func (_c *Authz_Authorize_Call) Return(err error) *Authz_Authorize_Call {
|
||||
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)
|
||||
return _c
|
||||
}
|
||||
|
||||
+14
-8
@@ -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
|
||||
func (_mock *Service) Authorize(ctx context.Context, pr policies.Policy) error {
|
||||
ret := _mock.Called(ctx, pr)
|
||||
func (_mock *Service) Authorize(ctx context.Context, pr policies.Policy, patAuthz *auth.PATAuthz) error {
|
||||
ret := _mock.Called(ctx, pr, patAuthz)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Authorize")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, policies.Policy) error); ok {
|
||||
r0 = returnFunc(ctx, pr)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, policies.Policy, *auth.PATAuthz) error); ok {
|
||||
r0 = returnFunc(ctx, pr, patAuthz)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
@@ -138,11 +138,12 @@ type Service_Authorize_Call struct {
|
||||
// Authorize is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - pr policies.Policy
|
||||
func (_e *Service_Expecter) Authorize(ctx interface{}, pr interface{}) *Service_Authorize_Call {
|
||||
return &Service_Authorize_Call{Call: _e.mock.On("Authorize", ctx, pr)}
|
||||
// - patAuthz *auth.PATAuthz
|
||||
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) {
|
||||
var arg0 context.Context
|
||||
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 {
|
||||
arg1 = args[1].(policies.Policy)
|
||||
}
|
||||
var arg2 *auth.PATAuthz
|
||||
if args[2] != nil {
|
||||
arg2 = args[2].(*auth.PATAuthz)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
@@ -165,7 +171,7 @@ func (_c *Service_Authorize_Call) Return(err error) *Service_Authorize_Call {
|
||||
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)
|
||||
return _c
|
||||
}
|
||||
|
||||
+16
@@ -69,6 +69,7 @@ const (
|
||||
DashboardType
|
||||
MessagesType
|
||||
DomainsType
|
||||
UsersType
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -78,6 +79,7 @@ const (
|
||||
DashboardsStr = "dashboards"
|
||||
MessagesStr = "messages"
|
||||
DomainsStr = "domains"
|
||||
UsersStr = "users"
|
||||
)
|
||||
|
||||
func (et EntityType) String() string {
|
||||
@@ -94,6 +96,8 @@ func (et EntityType) String() string {
|
||||
return MessagesStr
|
||||
case DomainsType:
|
||||
return DomainsStr
|
||||
case UsersType:
|
||||
return UsersStr
|
||||
default:
|
||||
return fmt.Sprintf("unknown domain entity type %d", et)
|
||||
}
|
||||
@@ -113,6 +117,8 @@ func ParseEntityType(et string) (EntityType, error) {
|
||||
return MessagesType, nil
|
||||
case DomainsStr:
|
||||
return DomainsType, nil
|
||||
case UsersStr:
|
||||
return UsersType, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown domain entity type %s", et)
|
||||
}
|
||||
@@ -280,6 +286,16 @@ func (s *Scope) Validate() error {
|
||||
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.
|
||||
type PAT struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
|
||||
+7
-11
@@ -56,7 +56,7 @@ type Authz interface {
|
||||
// `object`. Authorize returns a non-nil error if the subject has
|
||||
// no relation on the object (which simply means the operation is
|
||||
// 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
|
||||
@@ -205,13 +205,9 @@ func (svc service) RetrieveJWKS() []PublicKeyInfo {
|
||||
return keys
|
||||
}
|
||||
|
||||
func (svc service) Authorize(ctx context.Context, pr policies.Policy) error {
|
||||
if pr.PatID != "" {
|
||||
entityType, err := ParseEntityType(pr.EntityType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.AuthorizePAT(ctx, pr.UserID, pr.PatID, entityType, pr.Domain, pr.Operation, pr.EntityID); err != nil {
|
||||
func (svc service) Authorize(ctx context.Context, pr policies.Policy, patAuthz *PATAuthz) error {
|
||||
if patAuthz != nil {
|
||||
if err := svc.AuthorizePAT(ctx, patAuthz.UserID, patAuthz.PatID, patAuthz.EntityType, patAuthz.Domain, patAuthz.Operation, patAuthz.EntityID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -336,7 +332,7 @@ func (svc service) checkUserRole(ctx context.Context, key Key) (err error) {
|
||||
Permission: policies.AdminPermission,
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
}); err != nil {
|
||||
}, nil); err != nil {
|
||||
return errRoleAuth
|
||||
}
|
||||
return nil
|
||||
@@ -347,7 +343,7 @@ func (svc service) checkUserRole(ctx context.Context, key Key) (err error) {
|
||||
Permission: policies.MembershipPermission,
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
}); err != nil {
|
||||
}, nil); err != nil {
|
||||
return errRoleAuth
|
||||
}
|
||||
return nil
|
||||
@@ -364,7 +360,7 @@ func (svc service) getUserRole(ctx context.Context, userID string) (role Role) {
|
||||
Permission: policies.AdminPermission,
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
}); err == nil {
|
||||
}, nil); err == nil {
|
||||
rl = AdminRole
|
||||
}
|
||||
|
||||
|
||||
+105
-72
@@ -648,19 +648,20 @@ func TestAuthorize(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
policyReq policies.Policy
|
||||
patAuthz *auth.PATAuthz
|
||||
checkDomainPolicyReq policies.Policy
|
||||
checkPolicyReq policies.Policy
|
||||
patEntityType auth.EntityType
|
||||
patScopeErr error
|
||||
expectPATCheck bool
|
||||
expectCheckPolicy bool
|
||||
patErr error
|
||||
parseReq string
|
||||
parseRes auth.Key
|
||||
parseErr error
|
||||
callBackErr error
|
||||
checkPolicyErr error
|
||||
checkDomainPolicyErr error
|
||||
authorizePATErr error
|
||||
err error
|
||||
}{
|
||||
|
||||
{
|
||||
desc: "authorize a user key successfully",
|
||||
policyReq: policies.Policy{
|
||||
@@ -677,41 +678,51 @@ func TestAuthorize(t *testing.T) {
|
||||
ObjectType: policies.PlatformType,
|
||||
Permission: policies.AdminPermission,
|
||||
},
|
||||
expectCheckPolicy: true,
|
||||
err: nil,
|
||||
checkDomainPolicyReq: policies.Policy{
|
||||
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{
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
Permission: policies.AdminPermission,
|
||||
PatID: validID,
|
||||
UserID: userID,
|
||||
EntityType: auth.ChannelsScopeStr,
|
||||
Subject: userID,
|
||||
Object: validID,
|
||||
ObjectType: policies.ClientType,
|
||||
Permission: policies.ViewPermission,
|
||||
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{
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
Permission: policies.AdminPermission,
|
||||
PatID: validID,
|
||||
UserID: userID,
|
||||
EntityType: auth.ChannelsScopeStr,
|
||||
Subject: userID,
|
||||
Object: validID,
|
||||
ObjectType: policies.ClientType,
|
||||
Permission: policies.ViewPermission,
|
||||
Domain: domainID,
|
||||
Operation: auth.OpListChannels,
|
||||
EntityID: auth.AnyIDs,
|
||||
},
|
||||
patEntityType: auth.ChannelsType,
|
||||
expectPATCheck: true,
|
||||
expectCheckPolicy: true,
|
||||
err: nil,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "authorize with PAT scope check failure",
|
||||
@@ -721,12 +732,14 @@ func TestAuthorize(t *testing.T) {
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
Permission: policies.AdminPermission,
|
||||
PatID: validID,
|
||||
UserID: userID,
|
||||
EntityType: auth.ChannelsScopeStr,
|
||||
Domain: domainID,
|
||||
Operation: auth.OpListChannels,
|
||||
EntityID: auth.AnyIDs,
|
||||
},
|
||||
patAuthz: &auth.PATAuthz{
|
||||
PatID: validID,
|
||||
UserID: userID,
|
||||
EntityType: auth.ChannelsType,
|
||||
Domain: domainID,
|
||||
Operation: auth.OpListChannels,
|
||||
EntityID: auth.AnyIDs,
|
||||
},
|
||||
checkPolicyReq: policies.Policy{
|
||||
SubjectType: policies.UserType,
|
||||
@@ -734,17 +747,9 @@ func TestAuthorize(t *testing.T) {
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
Permission: policies.AdminPermission,
|
||||
PatID: validID,
|
||||
UserID: userID,
|
||||
EntityType: auth.ChannelsScopeStr,
|
||||
Domain: domainID,
|
||||
Operation: auth.OpListChannels,
|
||||
EntityID: auth.AnyIDs,
|
||||
},
|
||||
patEntityType: auth.ChannelsType,
|
||||
patScopeErr: repoerr.ErrNotFound,
|
||||
expectPATCheck: true,
|
||||
err: svcerr.ErrAuthorization,
|
||||
patErr: svcerr.ErrAuthorization,
|
||||
err: svcerr.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "authorize with invalid PAT entity type",
|
||||
@@ -754,12 +759,14 @@ func TestAuthorize(t *testing.T) {
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
Permission: policies.AdminPermission,
|
||||
PatID: validID,
|
||||
UserID: userID,
|
||||
EntityType: "invalid",
|
||||
Domain: domainID,
|
||||
Operation: auth.OpListChannels,
|
||||
EntityID: auth.AnyIDs,
|
||||
},
|
||||
patAuthz: &auth.PATAuthz{
|
||||
PatID: validID,
|
||||
UserID: userID,
|
||||
EntityType: auth.EntityType(100),
|
||||
Domain: domainID,
|
||||
Operation: auth.OpListChannels,
|
||||
EntityID: auth.AnyIDs,
|
||||
},
|
||||
checkPolicyReq: policies.Policy{
|
||||
SubjectType: policies.UserType,
|
||||
@@ -767,32 +774,55 @@ func TestAuthorize(t *testing.T) {
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
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{
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Object: "invalid",
|
||||
ObjectType: policies.PlatformType,
|
||||
Permission: policies.AdminPermission,
|
||||
Subject: userID,
|
||||
Object: validID,
|
||||
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,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Object: "invalid",
|
||||
ObjectType: policies.PlatformType,
|
||||
Permission: policies.AdminPermission,
|
||||
Subject: userID,
|
||||
Object: validID,
|
||||
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",
|
||||
@@ -810,23 +840,26 @@ func TestAuthorize(t *testing.T) {
|
||||
ObjectType: policies.PlatformType,
|
||||
Permission: policies.AdminPermission,
|
||||
},
|
||||
checkPolicyErr: repoerr.ErrNotFound,
|
||||
expectCheckPolicy: true,
|
||||
err: svcerr.ErrAuthorization,
|
||||
checkPolicyErr: repoerr.ErrNotFound,
|
||||
err: svcerr.ErrAuthorization,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
var policyCall *mock.Call
|
||||
if tc.expectCheckPolicy {
|
||||
if tc.checkPolicyReq != (policies.Policy{}) {
|
||||
policyCall = pEvaluator.On("CheckPolicy", mock.Anything, tc.checkPolicyReq).Return(tc.checkPolicyErr)
|
||||
}
|
||||
var patCall *mock.Call
|
||||
if tc.expectPATCheck {
|
||||
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)
|
||||
if tc.patAuthz != nil {
|
||||
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)
|
||||
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))
|
||||
if policyCall != nil {
|
||||
policyCall.Unset()
|
||||
|
||||
@@ -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 {
|
||||
req.UserID = session.UserID
|
||||
req.PatID = session.PatID
|
||||
req.Domain = session.DomainID
|
||||
|
||||
perm, err := am.entitiesOps.GetPermission(entityType, op)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -336,14 +335,24 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.
|
||||
|
||||
req.Permission = perm.String()
|
||||
|
||||
req.EntityID = req.Object
|
||||
req.EntityType = auth.ChannelsType.String()
|
||||
req.Operation = am.entitiesOps.OperationName(entityType, op)
|
||||
if op == operations.OpListUserChannels || op == dOperations.OpCreateDomainChannels || op == dOperations.OpListDomainChannels {
|
||||
req.EntityID = auth.AnyIDs
|
||||
var pat *smqauthz.PATReq
|
||||
if session.PatID != "" {
|
||||
entityID := req.Object
|
||||
opName := am.entitiesOps.OperationName(entityType, op)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -360,7 +369,7 @@ func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, session
|
||||
Permission: policies.AdminPermission,
|
||||
ObjectType: policies.PlatformType,
|
||||
Object: policies.SuperMQObject,
|
||||
}); err != nil {
|
||||
}, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -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 {
|
||||
req.UserID = session.UserID
|
||||
req.PatID = session.PatID
|
||||
req.Domain = session.DomainID
|
||||
|
||||
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.EntityID = req.Object
|
||||
req.EntityType = auth.ClientsType.String()
|
||||
req.Operation = am.entitiesOps.OperationName(entityType, op)
|
||||
if op == operations.OpListUserClients || op == dOperations.OpCreateDomainClients || op == dOperations.OpListDomainClients {
|
||||
req.EntityID = auth.AnyIDs
|
||||
var pat *smqauthz.PATReq
|
||||
if session.PatID != "" {
|
||||
entityID := req.Object
|
||||
opName := am.entitiesOps.OperationName(entityType, op)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -292,7 +300,7 @@ func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, session
|
||||
Permission: policies.AdminPermission,
|
||||
ObjectType: policies.PlatformType,
|
||||
Object: policies.SuperMQObject,
|
||||
}); err != nil {
|
||||
}, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
+1
-1
@@ -412,7 +412,7 @@ func createAdminPolicy(ctx context.Context, userID string, authz smqauthz.Author
|
||||
Permission: policies.AdministratorRelation,
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
}); err != nil {
|
||||
}, nil); err != nil {
|
||||
err := policyService.AddPolicy(ctx, policies.Policy{
|
||||
SubjectType: policies.UserType,
|
||||
Subject: userID,
|
||||
|
||||
@@ -6,6 +6,7 @@ package middleware
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/absmach/supermq/auth"
|
||||
"github.com/absmach/supermq/domains"
|
||||
"github.com/absmach/supermq/domains/operations"
|
||||
"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)
|
||||
}
|
||||
|
||||
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,
|
||||
SubjectType: policies.UserType,
|
||||
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) {
|
||||
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,
|
||||
SubjectType: policies.UserType,
|
||||
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) {
|
||||
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,
|
||||
SubjectType: policies.UserType,
|
||||
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) {
|
||||
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,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
@@ -125,7 +126,7 @@ func (am *authorizationMiddleware) FreezeDomain(ctx context.Context, session aut
|
||||
Permission: policies.AdminPermission,
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
}); err != nil {
|
||||
}, nil); err != nil {
|
||||
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) {
|
||||
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,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
@@ -151,6 +152,10 @@ func (am *authorizationMiddleware) SendInvitation(ctx context.Context, session a
|
||||
return domains.Invitation{}, err
|
||||
}
|
||||
|
||||
if err := am.checkAdmin(ctx, session); err != nil {
|
||||
return domains.Invitation{}, err
|
||||
}
|
||||
|
||||
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) {
|
||||
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,
|
||||
SubjectType: policies.UserType,
|
||||
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) {
|
||||
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,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
@@ -194,7 +199,9 @@ func (am *authorizationMiddleware) DeleteInvitation(ctx context.Context, session
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -202,13 +209,57 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, entityType str
|
||||
|
||||
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 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 {
|
||||
if session.Role != authn.AdminRole {
|
||||
return svcerr.ErrSuperAdminAction
|
||||
@@ -219,7 +270,7 @@ func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, session
|
||||
Permission: policies.AdminPermission,
|
||||
ObjectType: policies.PlatformType,
|
||||
Object: policies.SuperMQObject,
|
||||
}); err != nil {
|
||||
}, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -376,15 +376,13 @@ func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, session
|
||||
Permission: policies.AdminPermission,
|
||||
ObjectType: policies.PlatformType,
|
||||
Object: policies.SuperMQObject,
|
||||
}); err != nil {
|
||||
}, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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.EntityID = pr.Object
|
||||
pr.EntityType = auth.GroupsType.String()
|
||||
pr.Operation = am.entitiesOps.OperationName(entityType, op)
|
||||
if op == dOperations.OpListDomainGroups || op == dOperations.OpCreateDomainGroups {
|
||||
pr.EntityID = auth.AnyIDs
|
||||
var pat *smqauthz.PATReq
|
||||
if session.PatID != "" {
|
||||
entityID := pr.Object
|
||||
opName := am.entitiesOps.OperationName(entityType, op)
|
||||
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 nil
|
||||
|
||||
@@ -9,7 +9,7 @@ option go_package = "github.com/absmach/supermq/api/grpc/auth/v1";
|
||||
// AuthService is a service that provides authentication
|
||||
// and authorization functionalities for SuperMQ services.
|
||||
service AuthService {
|
||||
rpc Authorize(PolicyReq) returns (AuthZRes) {}
|
||||
rpc Authorize(AuthZReq) returns (AuthZRes) {}
|
||||
rpc Authenticate(AuthNReq) returns (AuthNRes) {}
|
||||
}
|
||||
|
||||
@@ -35,11 +35,20 @@ message PolicyReq {
|
||||
string permission = 7;
|
||||
string object = 8;
|
||||
string object_type = 9;
|
||||
string pat_id = 10;
|
||||
string operation = 11;
|
||||
string user_id = 12;
|
||||
string entity_id = 13;
|
||||
string entity_type = 14;
|
||||
}
|
||||
|
||||
message PATReq {
|
||||
string pat_id = 1;
|
||||
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 {
|
||||
|
||||
@@ -58,7 +58,7 @@ func (am *authorizationMiddleware) RetrieveAll(ctx context.Context, session smqa
|
||||
ObjectType: objectType,
|
||||
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
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func (am *authorizationMiddleware) RetrieveClientTelemetry(ctx context.Context,
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
+48
-34
@@ -46,7 +46,7 @@ func NewAuthorization(ctx context.Context, cfg grpcclient.Config, domainsAuthz p
|
||||
}, 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) {
|
||||
domainID := pr.Domain
|
||||
if domainID == "" {
|
||||
@@ -60,21 +60,29 @@ func (a authorization) Authorize(ctx context.Context, pr authz.PolicyReq) error
|
||||
}
|
||||
}
|
||||
|
||||
req := grpcAuthV1.PolicyReq{
|
||||
Domain: pr.Domain,
|
||||
SubjectType: pr.SubjectType,
|
||||
SubjectKind: pr.SubjectKind,
|
||||
SubjectRelation: pr.SubjectRelation,
|
||||
Subject: pr.Subject,
|
||||
Relation: pr.Relation,
|
||||
Permission: pr.Permission,
|
||||
Object: pr.Object,
|
||||
ObjectType: pr.ObjectType,
|
||||
PatId: pr.PatID,
|
||||
Operation: pr.Operation,
|
||||
UserId: pr.UserID,
|
||||
EntityId: pr.EntityID,
|
||||
EntityType: pr.EntityType,
|
||||
req := grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Domain: pr.Domain,
|
||||
SubjectType: pr.SubjectType,
|
||||
SubjectKind: pr.SubjectKind,
|
||||
SubjectRelation: pr.SubjectRelation,
|
||||
Subject: pr.Subject,
|
||||
Relation: pr.Relation,
|
||||
Permission: pr.Permission,
|
||||
Object: pr.Object,
|
||||
ObjectType: pr.ObjectType,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -95,32 +103,38 @@ func (a authorization) checkDomain(ctx context.Context, subjectType, subject, do
|
||||
|
||||
switch status {
|
||||
case domains.FreezeStatus:
|
||||
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.PolicyReq{
|
||||
Subject: subject,
|
||||
SubjectType: subjectType,
|
||||
Permission: policies.AdminPermission,
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: subject,
|
||||
SubjectType: subjectType,
|
||||
Permission: policies.AdminPermission,
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
},
|
||||
})
|
||||
|
||||
return err
|
||||
case domains.DisabledStatus:
|
||||
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.PolicyReq{
|
||||
Subject: subject,
|
||||
SubjectType: subjectType,
|
||||
Permission: policies.AdminPermission,
|
||||
Object: domainID,
|
||||
ObjectType: policies.DomainType,
|
||||
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: subject,
|
||||
SubjectType: subjectType,
|
||||
Permission: policies.AdminPermission,
|
||||
Object: domainID,
|
||||
ObjectType: policies.DomainType,
|
||||
},
|
||||
})
|
||||
|
||||
return err
|
||||
case domains.EnabledStatus:
|
||||
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.PolicyReq{
|
||||
Subject: subject,
|
||||
SubjectType: subjectType,
|
||||
Permission: policies.MembershipPermission,
|
||||
Object: domainID,
|
||||
ObjectType: policies.DomainType,
|
||||
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.AuthZReq{
|
||||
PolicyReq: &grpcAuthV1.PolicyReq{
|
||||
Subject: subject,
|
||||
SubjectType: subjectType,
|
||||
Permission: policies.MembershipPermission,
|
||||
Object: domainID,
|
||||
ObjectType: policies.DomainType,
|
||||
},
|
||||
})
|
||||
|
||||
return err
|
||||
|
||||
+21
-8
@@ -42,17 +42,30 @@ type PolicyReq struct {
|
||||
// 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.
|
||||
Permission string `json:"permission,omitempty"`
|
||||
}
|
||||
|
||||
// PAT authorization fields
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
PatID string `json:"pat_id,omitempty"`
|
||||
EntityType string `json:"entity_type,omitempty"`
|
||||
DomainID string `json:"domain_id,omitempty"`
|
||||
Operation string `json:"operation,omitempty"`
|
||||
EntityID string `json:"entity_id,omitempty"`
|
||||
// PATReq represents a Personal Access Token authorization request.
|
||||
type PATReq struct {
|
||||
// PatID contains the personal access token ID.
|
||||
PatID string `json:"pat_id"`
|
||||
|
||||
// Domain contains the domain ID.
|
||||
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.
|
||||
type Authorization interface {
|
||||
Authorize(ctx context.Context, pr PolicyReq) error
|
||||
Authorize(ctx context.Context, pr PolicyReq, pat *PATReq) error
|
||||
}
|
||||
|
||||
@@ -43,16 +43,16 @@ func (_m *Authorization) EXPECT() *Authorization_Expecter {
|
||||
}
|
||||
|
||||
// Authorize provides a mock function for the type Authorization
|
||||
func (_mock *Authorization) Authorize(ctx context.Context, pr authz.PolicyReq) error {
|
||||
ret := _mock.Called(ctx, pr)
|
||||
func (_mock *Authorization) Authorize(ctx context.Context, pr authz.PolicyReq, pat *authz.PATReq) error {
|
||||
ret := _mock.Called(ctx, pr, pat)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Authorize")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authz.PolicyReq) error); ok {
|
||||
r0 = returnFunc(ctx, pr)
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, authz.PolicyReq, *authz.PATReq) error); ok {
|
||||
r0 = returnFunc(ctx, pr, pat)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
@@ -67,11 +67,12 @@ type Authorization_Authorize_Call struct {
|
||||
// Authorize is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - pr authz.PolicyReq
|
||||
func (_e *Authorization_Expecter) Authorize(ctx interface{}, pr interface{}) *Authorization_Authorize_Call {
|
||||
return &Authorization_Authorize_Call{Call: _e.mock.On("Authorize", ctx, pr)}
|
||||
// - pat *authz.PATReq
|
||||
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) {
|
||||
var arg0 context.Context
|
||||
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 {
|
||||
arg1 = args[1].(authz.PolicyReq)
|
||||
}
|
||||
var arg2 *authz.PATReq
|
||||
if args[2] != nil {
|
||||
arg2 = args[2].(*authz.PATReq)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
@@ -94,7 +100,7 @@ func (_c *Authorization_Authorize_Call) Return(err error) *Authorization_Authori
|
||||
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)
|
||||
return _c
|
||||
}
|
||||
|
||||
@@ -49,19 +49,6 @@ type Policy struct {
|
||||
// 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.
|
||||
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 {
|
||||
|
||||
@@ -5,6 +5,7 @@ package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/absmach/supermq/auth"
|
||||
"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 {
|
||||
pr.UserID = session.UserID
|
||||
pr.PatID = session.PatID
|
||||
pr.Domain = session.DomainID
|
||||
|
||||
perm, err := ram.ops.GetPermission(op)
|
||||
@@ -304,19 +303,31 @@ func (ram RoleManagerAuthorizationMiddleware) authorize(ctx context.Context, ses
|
||||
|
||||
pr.Permission = perm.String()
|
||||
|
||||
pr.EntityID = pr.Object
|
||||
opName := ram.ops.OperationName(op)
|
||||
switch pr.ObjectType {
|
||||
case policies.GroupType:
|
||||
pr.EntityType = auth.GroupsType.String()
|
||||
case policies.ClientType:
|
||||
pr.EntityType = auth.ClientsType.String()
|
||||
case policies.ChannelType:
|
||||
pr.EntityType = auth.ChannelsType.String()
|
||||
var pat *smqauthz.PATReq
|
||||
if session.PatID != "" {
|
||||
opName := ram.ops.OperationName(op)
|
||||
var patEntityType string
|
||||
switch pr.ObjectType {
|
||||
case policies.GroupType:
|
||||
patEntityType = auth.GroupsType.String()
|
||||
case policies.ClientType:
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -338,7 +349,7 @@ func (ram RoleManagerAuthorizationMiddleware) validateMembers(ctx context.Contex
|
||||
SubjectKind: policies.UsersKind,
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
}); err != nil {
|
||||
}, nil); err != nil {
|
||||
return errors.Wrap(errors.ErrMissingMember, err)
|
||||
}
|
||||
}
|
||||
@@ -353,7 +364,7 @@ func (ram RoleManagerAuthorizationMiddleware) validateMembers(ctx context.Contex
|
||||
SubjectKind: policies.UsersKind,
|
||||
Object: session.DomainID,
|
||||
ObjectType: policies.DomainType,
|
||||
}); err != nil {
|
||||
}, nil); err != nil {
|
||||
return errors.Wrap(errors.ErrMissingDomainMember, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
|
||||
grpcTokenV1 "github.com/absmach/supermq/api/grpc/token/v1"
|
||||
"github.com/absmach/supermq/auth"
|
||||
"github.com/absmach/supermq/pkg/authn"
|
||||
smqauthz "github.com/absmach/supermq/pkg/authz"
|
||||
svcerr "github.com/absmach/supermq/pkg/errors/service"
|
||||
@@ -190,7 +191,7 @@ func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, session
|
||||
Permission: policies.AdminPermission,
|
||||
ObjectType: policies.PlatformType,
|
||||
Object: policies.SuperMQObject,
|
||||
}); err != nil {
|
||||
}, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -207,7 +208,19 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.
|
||||
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 nil
|
||||
|
||||
Reference in New Issue
Block a user