From c19440c7c52280097580196037dcada73e0eaf0a Mon Sep 17 00:00:00 2001 From: suryanshu185 Date: Thu, 23 Oct 2025 11:11:06 +0530 Subject: [PATCH 1/3] feat: Add custom roles API stubs - Add org-unit-role.proto with 5 RPC methods - Generate gRPC/gateway code and routes - Add stub server implementation - Update Swagger documentation - ListOrgUnitRoles returns built-in roles (admin, auditor, default) - CRUD operations return Unimplemented status Signed-off-by: suryanshu185 --- api/org-unit-role.pb.go | 800 ++++++++++++++++++++++++++++++- api/org-unit-role.pb.gw.go | 362 ++++++++++++++ api/org-unit-role.pb.route.go | 28 ++ api/org-unit-role.proto | 197 +++++++- api/org-unit-role_grpc.pb.go | 164 ++++++- api/swagger/apidocs.swagger.json | 343 ++++++++++++- pkg/server/org-unit-role.go | 53 ++ 7 files changed, 1925 insertions(+), 22 deletions(-) diff --git a/api/org-unit-role.pb.go b/api/org-unit-role.pb.go index 91aaea6..b0e84d4 100644 --- a/api/org-unit-role.pb.go +++ b/api/org-unit-role.pb.go @@ -77,7 +77,15 @@ type OrgUnitRolesListEntry struct { // role name, unique in an Org Unit Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // role description, provding details about the role - Desc string `protobuf:"bytes,2,opt,name=desc,proto3" json:"desc,omitempty"` + Desc string `protobuf:"bytes,2,opt,name=desc,proto3" json:"desc,omitempty"` + // role type: "built-in" for system roles (admin, auditor) or "custom" for user-defined roles + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` + // display name for custom roles (only applicable for custom roles) + DisplayName string `protobuf:"bytes,4,opt,name=displayName,proto3" json:"displayName,omitempty"` + // creation timestamp for custom roles (only applicable for custom roles) + Created int64 `protobuf:"varint,5,opt,name=created,proto3" json:"created,omitempty"` + // user who created the role for custom roles (only applicable for custom roles) + CreatedBy string `protobuf:"bytes,6,opt,name=createdBy,proto3" json:"createdBy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -126,7 +134,35 @@ func (x *OrgUnitRolesListEntry) GetDesc() string { return "" } -// org unit roles list response +func (x *OrgUnitRolesListEntry) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *OrgUnitRolesListEntry) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *OrgUnitRolesListEntry) GetCreated() int64 { + if x != nil { + return x.Created + } + return 0 +} + +func (x *OrgUnitRolesListEntry) GetCreatedBy() string { + if x != nil { + return x.CreatedBy + } + return "" +} + +// org unit roles list response - includes both built-in and custom roles type OrgUnitRolesListResp struct { state protoimpl.MessageState `protogen:"open.v1"` // list of roles available as part of the response @@ -172,21 +208,731 @@ func (x *OrgUnitRolesListResp) GetItems() []*OrgUnitRolesListEntry { return nil } +// Resource matching criteria for fine-grained permission control +type ResourceMatch struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Matching criteria type: "exact", "prefix", "suffix", "regex", "wildcard" + // - exact: Exact resource name match + // - prefix: Resource name starts with the key + // - suffix: Resource name ends with the key + // - regex: Resource name matches the regex pattern in key + // - wildcard: Key with * for wildcard matching (default if empty) + Criteria string `protobuf:"bytes,1,opt,name=criteria,proto3" json:"criteria,omitempty"` + // The matching key/pattern based on criteria + // For wildcard: supports * (e.g., "bucket-*", "*-prod", "*") + // For regex: valid regex pattern + // For exact/prefix/suffix: literal string + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ResourceMatch) Reset() { + *x = ResourceMatch{} + mi := &file_org_unit_role_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ResourceMatch) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResourceMatch) ProtoMessage() {} + +func (x *ResourceMatch) ProtoReflect() protoreflect.Message { + mi := &file_org_unit_role_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 ResourceMatch.ProtoReflect.Descriptor instead. +func (*ResourceMatch) Descriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{3} +} + +func (x *ResourceMatch) GetCriteria() string { + if x != nil { + return x.Criteria + } + return "" +} + +func (x *ResourceMatch) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +// Permission definition for custom roles +type RolePermission struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Resource name this permission applies to + Resource string `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` + // Resource matching criteria (optional, defaults to wildcard with key="*") + Match *ResourceMatch `protobuf:"bytes,2,opt,name=match,proto3" json:"match,omitempty"` + // List of allowed verbs/actions for this resource (supports "*" for all verbs) + Verbs []string `protobuf:"bytes,3,rep,name=verbs,proto3" json:"verbs,omitempty"` + // Action type: "Allow" or "Deny" (Deny takes precedence over Allow) + Action string `protobuf:"bytes,4,opt,name=action,proto3" json:"action,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RolePermission) Reset() { + *x = RolePermission{} + mi := &file_org_unit_role_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RolePermission) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RolePermission) ProtoMessage() {} + +func (x *RolePermission) ProtoReflect() protoreflect.Message { + mi := &file_org_unit_role_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 RolePermission.ProtoReflect.Descriptor instead. +func (*RolePermission) Descriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{4} +} + +func (x *RolePermission) GetResource() string { + if x != nil { + return x.Resource + } + return "" +} + +func (x *RolePermission) GetMatch() *ResourceMatch { + if x != nil { + return x.Match + } + return nil +} + +func (x *RolePermission) GetVerbs() []string { + if x != nil { + return x.Verbs + } + return nil +} + +func (x *RolePermission) GetAction() string { + if x != nil { + return x.Action + } + return "" +} + +// Create custom role request +type CreateCustomRoleReq struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Organization unit ID + Ou string `protobuf:"bytes,1,opt,name=ou,proto3" json:"ou,omitempty"` + // Name of the custom role (must be unique within the org unit) + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // Display name for the custom role + DisplayName string `protobuf:"bytes,3,opt,name=displayName,proto3" json:"displayName,omitempty"` + // Description explaining the purpose of this custom role + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + // List of permissions granted by this custom role + Permissions []*RolePermission `protobuf:"bytes,5,rep,name=permissions,proto3" json:"permissions,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateCustomRoleReq) Reset() { + *x = CreateCustomRoleReq{} + mi := &file_org_unit_role_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateCustomRoleReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateCustomRoleReq) ProtoMessage() {} + +func (x *CreateCustomRoleReq) ProtoReflect() protoreflect.Message { + mi := &file_org_unit_role_proto_msgTypes[5] + 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 CreateCustomRoleReq.ProtoReflect.Descriptor instead. +func (*CreateCustomRoleReq) Descriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{5} +} + +func (x *CreateCustomRoleReq) GetOu() string { + if x != nil { + return x.Ou + } + return "" +} + +func (x *CreateCustomRoleReq) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CreateCustomRoleReq) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *CreateCustomRoleReq) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *CreateCustomRoleReq) GetPermissions() []*RolePermission { + if x != nil { + return x.Permissions + } + return nil +} + +// Create custom role response +type CreateCustomRoleResp struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Confirmation message + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateCustomRoleResp) Reset() { + *x = CreateCustomRoleResp{} + mi := &file_org_unit_role_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateCustomRoleResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateCustomRoleResp) ProtoMessage() {} + +func (x *CreateCustomRoleResp) ProtoReflect() protoreflect.Message { + mi := &file_org_unit_role_proto_msgTypes[6] + 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 CreateCustomRoleResp.ProtoReflect.Descriptor instead. +func (*CreateCustomRoleResp) Descriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{6} +} + +func (x *CreateCustomRoleResp) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +// Update custom role request +type UpdateCustomRoleReq struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Organization unit ID + Ou string `protobuf:"bytes,1,opt,name=ou,proto3" json:"ou,omitempty"` + // Name of the custom role to update + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // Updated display name for the custom role + DisplayName string `protobuf:"bytes,3,opt,name=displayName,proto3" json:"displayName,omitempty"` + // Updated description explaining the purpose of this custom role + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + // Updated list of permissions granted by this custom role + Permissions []*RolePermission `protobuf:"bytes,5,rep,name=permissions,proto3" json:"permissions,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateCustomRoleReq) Reset() { + *x = UpdateCustomRoleReq{} + mi := &file_org_unit_role_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateCustomRoleReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateCustomRoleReq) ProtoMessage() {} + +func (x *UpdateCustomRoleReq) ProtoReflect() protoreflect.Message { + mi := &file_org_unit_role_proto_msgTypes[7] + 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 UpdateCustomRoleReq.ProtoReflect.Descriptor instead. +func (*UpdateCustomRoleReq) Descriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{7} +} + +func (x *UpdateCustomRoleReq) GetOu() string { + if x != nil { + return x.Ou + } + return "" +} + +func (x *UpdateCustomRoleReq) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *UpdateCustomRoleReq) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *UpdateCustomRoleReq) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *UpdateCustomRoleReq) GetPermissions() []*RolePermission { + if x != nil { + return x.Permissions + } + return nil +} + +// Update custom role response +type UpdateCustomRoleResp struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Confirmation message + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateCustomRoleResp) Reset() { + *x = UpdateCustomRoleResp{} + mi := &file_org_unit_role_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateCustomRoleResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateCustomRoleResp) ProtoMessage() {} + +func (x *UpdateCustomRoleResp) ProtoReflect() protoreflect.Message { + mi := &file_org_unit_role_proto_msgTypes[8] + 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 UpdateCustomRoleResp.ProtoReflect.Descriptor instead. +func (*UpdateCustomRoleResp) Descriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{8} +} + +func (x *UpdateCustomRoleResp) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +// Get custom role request +type GetCustomRoleReq struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Organization unit ID + Ou string `protobuf:"bytes,1,opt,name=ou,proto3" json:"ou,omitempty"` + // Name of the custom role to retrieve + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetCustomRoleReq) Reset() { + *x = GetCustomRoleReq{} + mi := &file_org_unit_role_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetCustomRoleReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCustomRoleReq) ProtoMessage() {} + +func (x *GetCustomRoleReq) ProtoReflect() protoreflect.Message { + mi := &file_org_unit_role_proto_msgTypes[9] + 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 GetCustomRoleReq.ProtoReflect.Descriptor instead. +func (*GetCustomRoleReq) Descriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{9} +} + +func (x *GetCustomRoleReq) GetOu() string { + if x != nil { + return x.Ou + } + return "" +} + +func (x *GetCustomRoleReq) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// Get custom role response +type GetCustomRoleResp struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Name of the custom role + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Display name for the custom role + DisplayName string `protobuf:"bytes,2,opt,name=displayName,proto3" json:"displayName,omitempty"` + // Description explaining the purpose of this custom role + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + // List of permissions granted by this custom role + Permissions []*RolePermission `protobuf:"bytes,4,rep,name=permissions,proto3" json:"permissions,omitempty"` + // Timestamp when the role was created + Created int64 `protobuf:"varint,5,opt,name=created,proto3" json:"created,omitempty"` + // User who created this custom role + CreatedBy string `protobuf:"bytes,6,opt,name=createdBy,proto3" json:"createdBy,omitempty"` + // Timestamp when the role was last updated + Updated int64 `protobuf:"varint,7,opt,name=updated,proto3" json:"updated,omitempty"` + // User who last updated this custom role + UpdatedBy string `protobuf:"bytes,8,opt,name=updatedBy,proto3" json:"updatedBy,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetCustomRoleResp) Reset() { + *x = GetCustomRoleResp{} + mi := &file_org_unit_role_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetCustomRoleResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCustomRoleResp) ProtoMessage() {} + +func (x *GetCustomRoleResp) ProtoReflect() protoreflect.Message { + mi := &file_org_unit_role_proto_msgTypes[10] + 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 GetCustomRoleResp.ProtoReflect.Descriptor instead. +func (*GetCustomRoleResp) Descriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{10} +} + +func (x *GetCustomRoleResp) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GetCustomRoleResp) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *GetCustomRoleResp) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *GetCustomRoleResp) GetPermissions() []*RolePermission { + if x != nil { + return x.Permissions + } + return nil +} + +func (x *GetCustomRoleResp) GetCreated() int64 { + if x != nil { + return x.Created + } + return 0 +} + +func (x *GetCustomRoleResp) GetCreatedBy() string { + if x != nil { + return x.CreatedBy + } + return "" +} + +func (x *GetCustomRoleResp) GetUpdated() int64 { + if x != nil { + return x.Updated + } + return 0 +} + +func (x *GetCustomRoleResp) GetUpdatedBy() string { + if x != nil { + return x.UpdatedBy + } + return "" +} + +// Delete custom role request +type DeleteCustomRoleReq struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Organization unit ID + Ou string `protobuf:"bytes,1,opt,name=ou,proto3" json:"ou,omitempty"` + // Name of the custom role to delete + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteCustomRoleReq) Reset() { + *x = DeleteCustomRoleReq{} + mi := &file_org_unit_role_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteCustomRoleReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteCustomRoleReq) ProtoMessage() {} + +func (x *DeleteCustomRoleReq) ProtoReflect() protoreflect.Message { + mi := &file_org_unit_role_proto_msgTypes[11] + 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 DeleteCustomRoleReq.ProtoReflect.Descriptor instead. +func (*DeleteCustomRoleReq) Descriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{11} +} + +func (x *DeleteCustomRoleReq) GetOu() string { + if x != nil { + return x.Ou + } + return "" +} + +func (x *DeleteCustomRoleReq) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// Delete custom role response +type DeleteCustomRoleResp struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Confirmation message + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteCustomRoleResp) Reset() { + *x = DeleteCustomRoleResp{} + mi := &file_org_unit_role_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteCustomRoleResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteCustomRoleResp) ProtoMessage() {} + +func (x *DeleteCustomRoleResp) ProtoReflect() protoreflect.Message { + mi := &file_org_unit_role_proto_msgTypes[12] + 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 DeleteCustomRoleResp.ProtoReflect.Descriptor instead. +func (*DeleteCustomRoleResp) Descriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{12} +} + +func (x *DeleteCustomRoleResp) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + var File_org_unit_role_proto protoreflect.FileDescriptor const file_org_unit_role_proto_rawDesc = "" + "\n" + "\x13org-unit-role.proto\x12\x03api\x1a\x1cgoogle/api/annotations.proto\x1a\x17coreapis/api/role.proto\"%\n" + "\x13OrgUnitRolesListReq\x12\x0e\n" + - "\x02ou\x18\x01 \x01(\tR\x02ou\"?\n" + + "\x02ou\x18\x01 \x01(\tR\x02ou\"\xad\x01\n" + "\x15OrgUnitRolesListEntry\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" + - "\x04desc\x18\x02 \x01(\tR\x04desc\"H\n" + + "\x04desc\x18\x02 \x01(\tR\x04desc\x12\x12\n" + + "\x04type\x18\x03 \x01(\tR\x04type\x12 \n" + + "\vdisplayName\x18\x04 \x01(\tR\vdisplayName\x12\x18\n" + + "\acreated\x18\x05 \x01(\x03R\acreated\x12\x1c\n" + + "\tcreatedBy\x18\x06 \x01(\tR\tcreatedBy\"H\n" + "\x14OrgUnitRolesListResp\x120\n" + - "\x05items\x18\x02 \x03(\v2\x1a.api.OrgUnitRolesListEntryR\x05items2\x98\x01\n" + + "\x05items\x18\x02 \x03(\v2\x1a.api.OrgUnitRolesListEntryR\x05items\"=\n" + + "\rResourceMatch\x12\x1a\n" + + "\bcriteria\x18\x01 \x01(\tR\bcriteria\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\"\x84\x01\n" + + "\x0eRolePermission\x12\x1a\n" + + "\bresource\x18\x01 \x01(\tR\bresource\x12(\n" + + "\x05match\x18\x02 \x01(\v2\x12.api.ResourceMatchR\x05match\x12\x14\n" + + "\x05verbs\x18\x03 \x03(\tR\x05verbs\x12\x16\n" + + "\x06action\x18\x04 \x01(\tR\x06action\"\xb4\x01\n" + + "\x13CreateCustomRoleReq\x12\x0e\n" + + "\x02ou\x18\x01 \x01(\tR\x02ou\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12 \n" + + "\vdisplayName\x18\x03 \x01(\tR\vdisplayName\x12 \n" + + "\vdescription\x18\x04 \x01(\tR\vdescription\x125\n" + + "\vpermissions\x18\x05 \x03(\v2\x13.api.RolePermissionR\vpermissions\"0\n" + + "\x14CreateCustomRoleResp\x12\x18\n" + + "\amessage\x18\x01 \x01(\tR\amessage\"\xb4\x01\n" + + "\x13UpdateCustomRoleReq\x12\x0e\n" + + "\x02ou\x18\x01 \x01(\tR\x02ou\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12 \n" + + "\vdisplayName\x18\x03 \x01(\tR\vdisplayName\x12 \n" + + "\vdescription\x18\x04 \x01(\tR\vdescription\x125\n" + + "\vpermissions\x18\x05 \x03(\v2\x13.api.RolePermissionR\vpermissions\"0\n" + + "\x14UpdateCustomRoleResp\x12\x18\n" + + "\amessage\x18\x01 \x01(\tR\amessage\"6\n" + + "\x10GetCustomRoleReq\x12\x0e\n" + + "\x02ou\x18\x01 \x01(\tR\x02ou\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\"\x92\x02\n" + + "\x11GetCustomRoleResp\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12 \n" + + "\vdisplayName\x18\x02 \x01(\tR\vdisplayName\x12 \n" + + "\vdescription\x18\x03 \x01(\tR\vdescription\x125\n" + + "\vpermissions\x18\x04 \x03(\v2\x13.api.RolePermissionR\vpermissions\x12\x18\n" + + "\acreated\x18\x05 \x01(\x03R\acreated\x12\x1c\n" + + "\tcreatedBy\x18\x06 \x01(\tR\tcreatedBy\x12\x18\n" + + "\aupdated\x18\a \x01(\x03R\aupdated\x12\x1c\n" + + "\tupdatedBy\x18\b \x01(\tR\tupdatedBy\"9\n" + + "\x13DeleteCustomRoleReq\x12\x0e\n" + + "\x02ou\x18\x01 \x01(\tR\x02ou\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\"0\n" + + "\x14DeleteCustomRoleResp\x12\x18\n" + + "\amessage\x18\x01 \x01(\tR\amessage2\xf3\x05\n" + "\vOrgUnitRole\x12\x88\x01\n" + "\x10ListOrgUnitRoles\x12\x18.api.OrgUnitRolesListReq\x1a\x19.api.OrgUnitRolesListResp\"?\x8a\xb5\x18\x19\n" + - "\rorg-unit-role\x12\x02ou\x1a\x04list\x82\xd3\xe4\x93\x02\x1c\x12\x1a/api/auth/v1/ou/{ou}/rolesB+Z)github.com/go-core-stack/auth-gateway/apib\x06proto3" + "\rorg-unit-role\x12\x02ou\x1a\x04list\x82\xd3\xe4\x93\x02\x1c\x12\x1a/api/auth/v1/ou/{ou}/roles\x12\x93\x01\n" + + "\x10CreateCustomRole\x12\x18.api.CreateCustomRoleReq\x1a\x19.api.CreateCustomRoleResp\"J\x8a\xb5\x18\"\n" + + "\x14org-unit-custom-role\x12\x02ou\x1a\x06create\x82\xd3\xe4\x93\x02\x1e:\x01*\"\x19/api/auth/v1/ou/{ou}/role\x12\x9a\x01\n" + + "\x10UpdateCustomRole\x12\x18.api.UpdateCustomRoleReq\x1a\x19.api.UpdateCustomRoleResp\"Q\x8a\xb5\x18\"\n" + + "\x14org-unit-custom-role\x12\x02ou\x1a\x06update\x82\xd3\xe4\x93\x02%:\x01*\x1a /api/auth/v1/ou/{ou}/role/{name}\x12\x8b\x01\n" + + "\rGetCustomRole\x12\x15.api.GetCustomRoleReq\x1a\x16.api.GetCustomRoleResp\"K\x8a\xb5\x18\x1f\n" + + "\x14org-unit-custom-role\x12\x02ou\x1a\x03get\x82\xd3\xe4\x93\x02\"\x12 /api/auth/v1/ou/{ou}/role/{name}\x12\x97\x01\n" + + "\x10DeleteCustomRole\x12\x18.api.DeleteCustomRoleReq\x1a\x19.api.DeleteCustomRoleResp\"N\x8a\xb5\x18\"\n" + + "\x14org-unit-custom-role\x12\x02ou\x1a\x06delete\x82\xd3\xe4\x93\x02\"* /api/auth/v1/ou/{ou}/role/{name}B+Z)github.com/go-core-stack/auth-gateway/apib\x06proto3" var ( file_org_unit_role_proto_rawDescOnce sync.Once @@ -200,21 +946,43 @@ func file_org_unit_role_proto_rawDescGZIP() []byte { return file_org_unit_role_proto_rawDescData } -var file_org_unit_role_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_org_unit_role_proto_msgTypes = make([]protoimpl.MessageInfo, 13) var file_org_unit_role_proto_goTypes = []any{ (*OrgUnitRolesListReq)(nil), // 0: api.OrgUnitRolesListReq (*OrgUnitRolesListEntry)(nil), // 1: api.OrgUnitRolesListEntry (*OrgUnitRolesListResp)(nil), // 2: api.OrgUnitRolesListResp + (*ResourceMatch)(nil), // 3: api.ResourceMatch + (*RolePermission)(nil), // 4: api.RolePermission + (*CreateCustomRoleReq)(nil), // 5: api.CreateCustomRoleReq + (*CreateCustomRoleResp)(nil), // 6: api.CreateCustomRoleResp + (*UpdateCustomRoleReq)(nil), // 7: api.UpdateCustomRoleReq + (*UpdateCustomRoleResp)(nil), // 8: api.UpdateCustomRoleResp + (*GetCustomRoleReq)(nil), // 9: api.GetCustomRoleReq + (*GetCustomRoleResp)(nil), // 10: api.GetCustomRoleResp + (*DeleteCustomRoleReq)(nil), // 11: api.DeleteCustomRoleReq + (*DeleteCustomRoleResp)(nil), // 12: api.DeleteCustomRoleResp } var file_org_unit_role_proto_depIdxs = []int32{ - 1, // 0: api.OrgUnitRolesListResp.items:type_name -> api.OrgUnitRolesListEntry - 0, // 1: api.OrgUnitRole.ListOrgUnitRoles:input_type -> api.OrgUnitRolesListReq - 2, // 2: api.OrgUnitRole.ListOrgUnitRoles:output_type -> api.OrgUnitRolesListResp - 2, // [2:3] is the sub-list for method output_type - 1, // [1:2] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 1, // 0: api.OrgUnitRolesListResp.items:type_name -> api.OrgUnitRolesListEntry + 3, // 1: api.RolePermission.match:type_name -> api.ResourceMatch + 4, // 2: api.CreateCustomRoleReq.permissions:type_name -> api.RolePermission + 4, // 3: api.UpdateCustomRoleReq.permissions:type_name -> api.RolePermission + 4, // 4: api.GetCustomRoleResp.permissions:type_name -> api.RolePermission + 0, // 5: api.OrgUnitRole.ListOrgUnitRoles:input_type -> api.OrgUnitRolesListReq + 5, // 6: api.OrgUnitRole.CreateCustomRole:input_type -> api.CreateCustomRoleReq + 7, // 7: api.OrgUnitRole.UpdateCustomRole:input_type -> api.UpdateCustomRoleReq + 9, // 8: api.OrgUnitRole.GetCustomRole:input_type -> api.GetCustomRoleReq + 11, // 9: api.OrgUnitRole.DeleteCustomRole:input_type -> api.DeleteCustomRoleReq + 2, // 10: api.OrgUnitRole.ListOrgUnitRoles:output_type -> api.OrgUnitRolesListResp + 6, // 11: api.OrgUnitRole.CreateCustomRole:output_type -> api.CreateCustomRoleResp + 8, // 12: api.OrgUnitRole.UpdateCustomRole:output_type -> api.UpdateCustomRoleResp + 10, // 13: api.OrgUnitRole.GetCustomRole:output_type -> api.GetCustomRoleResp + 12, // 14: api.OrgUnitRole.DeleteCustomRole:output_type -> api.DeleteCustomRoleResp + 10, // [10:15] is the sub-list for method output_type + 5, // [5:10] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_org_unit_role_proto_init() } @@ -228,7 +996,7 @@ func file_org_unit_role_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_org_unit_role_proto_rawDesc), len(file_org_unit_role_proto_rawDesc)), NumEnums: 0, - NumMessages: 3, + NumMessages: 13, NumExtensions: 0, NumServices: 1, }, diff --git a/api/org-unit-role.pb.gw.go b/api/org-unit-role.pb.gw.go index 0017338..1fe7a4c 100644 --- a/api/org-unit-role.pb.gw.go +++ b/api/org-unit-role.pb.gw.go @@ -72,6 +72,212 @@ func local_request_OrgUnitRole_ListOrgUnitRoles_0(ctx context.Context, marshaler return msg, metadata, err } +func request_OrgUnitRole_CreateCustomRole_0(ctx context.Context, marshaler runtime.Marshaler, client OrgUnitRoleClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq CreateCustomRoleReq + metadata runtime.ServerMetadata + err error + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + val, ok := pathParams["ou"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "ou") + } + protoReq.Ou, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "ou", err) + } + msg, err := client.CreateCustomRole(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_OrgUnitRole_CreateCustomRole_0(ctx context.Context, marshaler runtime.Marshaler, server OrgUnitRoleServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq CreateCustomRoleReq + metadata runtime.ServerMetadata + err error + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + val, ok := pathParams["ou"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "ou") + } + protoReq.Ou, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "ou", err) + } + msg, err := server.CreateCustomRole(ctx, &protoReq) + return msg, metadata, err +} + +func request_OrgUnitRole_UpdateCustomRole_0(ctx context.Context, marshaler runtime.Marshaler, client OrgUnitRoleClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq UpdateCustomRoleReq + metadata runtime.ServerMetadata + err error + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + val, ok := pathParams["ou"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "ou") + } + protoReq.Ou, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "ou", err) + } + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := client.UpdateCustomRole(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_OrgUnitRole_UpdateCustomRole_0(ctx context.Context, marshaler runtime.Marshaler, server OrgUnitRoleServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq UpdateCustomRoleReq + metadata runtime.ServerMetadata + err error + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + val, ok := pathParams["ou"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "ou") + } + protoReq.Ou, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "ou", err) + } + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := server.UpdateCustomRole(ctx, &protoReq) + return msg, metadata, err +} + +func request_OrgUnitRole_GetCustomRole_0(ctx context.Context, marshaler runtime.Marshaler, client OrgUnitRoleClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetCustomRoleReq + metadata runtime.ServerMetadata + err error + ) + io.Copy(io.Discard, req.Body) + val, ok := pathParams["ou"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "ou") + } + protoReq.Ou, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "ou", err) + } + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := client.GetCustomRole(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_OrgUnitRole_GetCustomRole_0(ctx context.Context, marshaler runtime.Marshaler, server OrgUnitRoleServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetCustomRoleReq + metadata runtime.ServerMetadata + err error + ) + val, ok := pathParams["ou"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "ou") + } + protoReq.Ou, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "ou", err) + } + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := server.GetCustomRole(ctx, &protoReq) + return msg, metadata, err +} + +func request_OrgUnitRole_DeleteCustomRole_0(ctx context.Context, marshaler runtime.Marshaler, client OrgUnitRoleClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq DeleteCustomRoleReq + metadata runtime.ServerMetadata + err error + ) + io.Copy(io.Discard, req.Body) + val, ok := pathParams["ou"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "ou") + } + protoReq.Ou, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "ou", err) + } + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := client.DeleteCustomRole(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_OrgUnitRole_DeleteCustomRole_0(ctx context.Context, marshaler runtime.Marshaler, server OrgUnitRoleServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq DeleteCustomRoleReq + metadata runtime.ServerMetadata + err error + ) + val, ok := pathParams["ou"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "ou") + } + protoReq.Ou, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "ou", err) + } + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + protoReq.Name, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + msg, err := server.DeleteCustomRole(ctx, &protoReq) + return msg, metadata, err +} + // RegisterOrgUnitRoleHandlerServer registers the http handlers for service OrgUnitRole to "mux". // UnaryRPC :call OrgUnitRoleServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -98,6 +304,86 @@ func RegisterOrgUnitRoleHandlerServer(ctx context.Context, mux *runtime.ServeMux } forward_OrgUnitRole_ListOrgUnitRoles_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodPost, pattern_OrgUnitRole_CreateCustomRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/api.OrgUnitRole/CreateCustomRole", runtime.WithHTTPPathPattern("/api/auth/v1/ou/{ou}/role")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_OrgUnitRole_CreateCustomRole_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_OrgUnitRole_CreateCustomRole_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodPut, pattern_OrgUnitRole_UpdateCustomRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/api.OrgUnitRole/UpdateCustomRole", runtime.WithHTTPPathPattern("/api/auth/v1/ou/{ou}/role/{name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_OrgUnitRole_UpdateCustomRole_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_OrgUnitRole_UpdateCustomRole_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_OrgUnitRole_GetCustomRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/api.OrgUnitRole/GetCustomRole", runtime.WithHTTPPathPattern("/api/auth/v1/ou/{ou}/role/{name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_OrgUnitRole_GetCustomRole_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_OrgUnitRole_GetCustomRole_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodDelete, pattern_OrgUnitRole_DeleteCustomRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/api.OrgUnitRole/DeleteCustomRole", runtime.WithHTTPPathPattern("/api/auth/v1/ou/{ou}/role/{name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_OrgUnitRole_DeleteCustomRole_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_OrgUnitRole_DeleteCustomRole_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) return nil } @@ -155,13 +441,89 @@ func RegisterOrgUnitRoleHandlerClient(ctx context.Context, mux *runtime.ServeMux } forward_OrgUnitRole_ListOrgUnitRoles_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodPost, pattern_OrgUnitRole_CreateCustomRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/api.OrgUnitRole/CreateCustomRole", runtime.WithHTTPPathPattern("/api/auth/v1/ou/{ou}/role")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_OrgUnitRole_CreateCustomRole_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_OrgUnitRole_CreateCustomRole_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodPut, pattern_OrgUnitRole_UpdateCustomRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/api.OrgUnitRole/UpdateCustomRole", runtime.WithHTTPPathPattern("/api/auth/v1/ou/{ou}/role/{name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_OrgUnitRole_UpdateCustomRole_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_OrgUnitRole_UpdateCustomRole_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_OrgUnitRole_GetCustomRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/api.OrgUnitRole/GetCustomRole", runtime.WithHTTPPathPattern("/api/auth/v1/ou/{ou}/role/{name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_OrgUnitRole_GetCustomRole_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_OrgUnitRole_GetCustomRole_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodDelete, pattern_OrgUnitRole_DeleteCustomRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/api.OrgUnitRole/DeleteCustomRole", runtime.WithHTTPPathPattern("/api/auth/v1/ou/{ou}/role/{name}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_OrgUnitRole_DeleteCustomRole_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_OrgUnitRole_DeleteCustomRole_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) return nil } var ( pattern_OrgUnitRole_ListOrgUnitRoles_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "auth", "v1", "ou", "roles"}, "")) + pattern_OrgUnitRole_CreateCustomRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "auth", "v1", "ou", "role"}, "")) + pattern_OrgUnitRole_UpdateCustomRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "auth", "v1", "ou", "role", "name"}, "")) + pattern_OrgUnitRole_GetCustomRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "auth", "v1", "ou", "role", "name"}, "")) + pattern_OrgUnitRole_DeleteCustomRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "auth", "v1", "ou", "role", "name"}, "")) ) var ( forward_OrgUnitRole_ListOrgUnitRoles_0 = runtime.ForwardResponseMessage + forward_OrgUnitRole_CreateCustomRole_0 = runtime.ForwardResponseMessage + forward_OrgUnitRole_UpdateCustomRole_0 = runtime.ForwardResponseMessage + forward_OrgUnitRole_GetCustomRole_0 = runtime.ForwardResponseMessage + forward_OrgUnitRole_DeleteCustomRole_0 = runtime.ForwardResponseMessage ) diff --git a/api/org-unit-role.pb.route.go b/api/org-unit-role.pb.route.go index 04bff78..c7ced4c 100644 --- a/api/org-unit-role.pb.route.go +++ b/api/org-unit-role.pb.route.go @@ -16,4 +16,32 @@ func init() { route.Scopes = append(route.Scopes, "ou") route.Verb = "list" RoutesOrgUnitRole = append(RoutesOrgUnitRole, route) + + // Adding Route information for CreateCustomRole RPC + route = model.NewRoute("/api/auth/v1/ou/{ou}/role", "POST") + route.Resource = "org-unit-custom-role" + route.Scopes = append(route.Scopes, "ou") + route.Verb = "create" + RoutesOrgUnitRole = append(RoutesOrgUnitRole, route) + + // Adding Route information for UpdateCustomRole RPC + route = model.NewRoute("/api/auth/v1/ou/{ou}/role/{name}", "PUT") + route.Resource = "org-unit-custom-role" + route.Scopes = append(route.Scopes, "ou") + route.Verb = "update" + RoutesOrgUnitRole = append(RoutesOrgUnitRole, route) + + // Adding Route information for GetCustomRole RPC + route = model.NewRoute("/api/auth/v1/ou/{ou}/role/{name}", "GET") + route.Resource = "org-unit-custom-role" + route.Scopes = append(route.Scopes, "ou") + route.Verb = "get" + RoutesOrgUnitRole = append(RoutesOrgUnitRole, route) + + // Adding Route information for DeleteCustomRole RPC + route = model.NewRoute("/api/auth/v1/ou/{ou}/role/{name}", "DELETE") + route.Resource = "org-unit-custom-role" + route.Scopes = append(route.Scopes, "ou") + route.Verb = "delete" + RoutesOrgUnitRole = append(RoutesOrgUnitRole, route) } diff --git a/api/org-unit-role.proto b/api/org-unit-role.proto index fcac3e0..023d6ab 100644 --- a/api/org-unit-role.proto +++ b/api/org-unit-role.proto @@ -12,7 +12,7 @@ option go_package = "github.com/go-core-stack/auth-gateway/api"; // Service provided to manage org unit roles service OrgUnitRole { - // Get List of available org unit roles, for specifc orgranisation + // Get List of available org unit roles (both built-in and custom), for specific organisation rpc ListOrgUnitRoles(OrgUnitRolesListReq) returns (OrgUnitRolesListResp) { option (google.api.http) = { get: "/api/auth/v1/ou/{ou}/roles" @@ -23,6 +23,56 @@ service OrgUnitRole { verb: "list" }; } + + // Create a new custom role for the organization unit + rpc CreateCustomRole(CreateCustomRoleReq) returns (CreateCustomRoleResp) { + option (google.api.http) = { + post: "/api/auth/v1/ou/{ou}/role" + body: "*" + }; + option (api.role) = { + resource: "org-unit-custom-role" + scope: "ou" + verb: "create" + }; + } + + // Update an existing custom role for the organization unit + rpc UpdateCustomRole(UpdateCustomRoleReq) returns (UpdateCustomRoleResp) { + option (google.api.http) = { + put: "/api/auth/v1/ou/{ou}/role/{name}" + body: "*" + }; + option (api.role) = { + resource: "org-unit-custom-role" + scope: "ou" + verb: "update" + }; + } + + // Get details of a specific custom role + rpc GetCustomRole(GetCustomRoleReq) returns (GetCustomRoleResp) { + option (google.api.http) = { + get: "/api/auth/v1/ou/{ou}/role/{name}" + }; + option (api.role) = { + resource: "org-unit-custom-role" + scope: "ou" + verb: "get" + }; + } + + // Delete (soft delete) a custom role from the organization unit + rpc DeleteCustomRole(DeleteCustomRoleReq) returns (DeleteCustomRoleResp) { + option (google.api.http) = { + delete: "/api/auth/v1/ou/{ou}/role/{name}" + }; + option (api.role) = { + resource: "org-unit-custom-role" + scope: "ou" + verb: "delete" + }; + } } // org unit roles list request @@ -37,9 +87,21 @@ message OrgUnitRolesListEntry { // role description, provding details about the role string desc = 2; + + // role type: "built-in" for system roles (admin, auditor) or "custom" for user-defined roles + string type = 3; + + // display name for custom roles (only applicable for custom roles) + string displayName = 4; + + // creation timestamp for custom roles (only applicable for custom roles) + int64 created = 5; + + // user who created the role for custom roles (only applicable for custom roles) + string createdBy = 6; } -// org unit roles list response +// org unit roles list response - includes both built-in and custom roles message OrgUnitRolesListResp { // eventually if we start working with longer list of roles // keep first index for count to provide pagination @@ -48,3 +110,134 @@ message OrgUnitRolesListResp { // list of roles available as part of the response repeated OrgUnitRolesListEntry items = 2; } + +// Resource matching criteria for fine-grained permission control +message ResourceMatch { + // Matching criteria type: "exact", "prefix", "suffix", "regex", "wildcard" + // - exact: Exact resource name match + // - prefix: Resource name starts with the key + // - suffix: Resource name ends with the key + // - regex: Resource name matches the regex pattern in key + // - wildcard: Key with * for wildcard matching (default if empty) + string criteria = 1; + + // The matching key/pattern based on criteria + // For wildcard: supports * (e.g., "bucket-*", "*-prod", "*") + // For regex: valid regex pattern + // For exact/prefix/suffix: literal string + string key = 2; +} + +// Permission definition for custom roles +message RolePermission { + // Resource name this permission applies to + string resource = 1; + + // Resource matching criteria (optional, defaults to wildcard with key="*") + ResourceMatch match = 2; + + // List of allowed verbs/actions for this resource (supports "*" for all verbs) + repeated string verbs = 3; + + // Action type: "Allow" or "Deny" (Deny takes precedence over Allow) + string action = 4; +} + +// Create custom role request +message CreateCustomRoleReq { + // Organization unit ID + string ou = 1; + + // Name of the custom role (must be unique within the org unit) + string name = 2; + + // Display name for the custom role + string displayName = 3; + + // Description explaining the purpose of this custom role + string description = 4; + + // List of permissions granted by this custom role + repeated RolePermission permissions = 5; +} + +// Create custom role response +message CreateCustomRoleResp { + // Confirmation message + string message = 1; +} + +// Update custom role request +message UpdateCustomRoleReq { + // Organization unit ID + string ou = 1; + + // Name of the custom role to update + string name = 2; + + // Updated display name for the custom role + string displayName = 3; + + // Updated description explaining the purpose of this custom role + string description = 4; + + // Updated list of permissions granted by this custom role + repeated RolePermission permissions = 5; +} + +// Update custom role response +message UpdateCustomRoleResp { + // Confirmation message + string message = 1; +} + +// Get custom role request +message GetCustomRoleReq { + // Organization unit ID + string ou = 1; + + // Name of the custom role to retrieve + string name = 2; +} + +// Get custom role response +message GetCustomRoleResp { + // Name of the custom role + string name = 1; + + // Display name for the custom role + string displayName = 2; + + // Description explaining the purpose of this custom role + string description = 3; + + // List of permissions granted by this custom role + repeated RolePermission permissions = 4; + + // Timestamp when the role was created + int64 created = 5; + + // User who created this custom role + string createdBy = 6; + + // Timestamp when the role was last updated + int64 updated = 7; + + // User who last updated this custom role + string updatedBy = 8; +} + +// Delete custom role request +message DeleteCustomRoleReq { + // Organization unit ID + string ou = 1; + + // Name of the custom role to delete + string name = 2; +} + +// Delete custom role response +message DeleteCustomRoleResp { + // Confirmation message + string message = 1; +} diff --git a/api/org-unit-role_grpc.pb.go b/api/org-unit-role_grpc.pb.go index d6d7d20..0782c89 100644 --- a/api/org-unit-role_grpc.pb.go +++ b/api/org-unit-role_grpc.pb.go @@ -23,6 +23,10 @@ const _ = grpc.SupportPackageIsVersion9 const ( OrgUnitRole_ListOrgUnitRoles_FullMethodName = "/api.OrgUnitRole/ListOrgUnitRoles" + OrgUnitRole_CreateCustomRole_FullMethodName = "/api.OrgUnitRole/CreateCustomRole" + OrgUnitRole_UpdateCustomRole_FullMethodName = "/api.OrgUnitRole/UpdateCustomRole" + OrgUnitRole_GetCustomRole_FullMethodName = "/api.OrgUnitRole/GetCustomRole" + OrgUnitRole_DeleteCustomRole_FullMethodName = "/api.OrgUnitRole/DeleteCustomRole" ) // OrgUnitRoleClient is the client API for OrgUnitRole service. @@ -31,8 +35,16 @@ const ( // // Service provided to manage org unit roles type OrgUnitRoleClient interface { - // Get List of available org unit roles, for specifc orgranisation + // Get List of available org unit roles (both built-in and custom), for specific organisation ListOrgUnitRoles(ctx context.Context, in *OrgUnitRolesListReq, opts ...grpc.CallOption) (*OrgUnitRolesListResp, error) + // Create a new custom role for the organization unit + CreateCustomRole(ctx context.Context, in *CreateCustomRoleReq, opts ...grpc.CallOption) (*CreateCustomRoleResp, error) + // Update an existing custom role for the organization unit + UpdateCustomRole(ctx context.Context, in *UpdateCustomRoleReq, opts ...grpc.CallOption) (*UpdateCustomRoleResp, error) + // Get details of a specific custom role + GetCustomRole(ctx context.Context, in *GetCustomRoleReq, opts ...grpc.CallOption) (*GetCustomRoleResp, error) + // Delete (soft delete) a custom role from the organization unit + DeleteCustomRole(ctx context.Context, in *DeleteCustomRoleReq, opts ...grpc.CallOption) (*DeleteCustomRoleResp, error) } type orgUnitRoleClient struct { @@ -53,14 +65,62 @@ func (c *orgUnitRoleClient) ListOrgUnitRoles(ctx context.Context, in *OrgUnitRol return out, nil } +func (c *orgUnitRoleClient) CreateCustomRole(ctx context.Context, in *CreateCustomRoleReq, opts ...grpc.CallOption) (*CreateCustomRoleResp, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateCustomRoleResp) + err := c.cc.Invoke(ctx, OrgUnitRole_CreateCustomRole_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *orgUnitRoleClient) UpdateCustomRole(ctx context.Context, in *UpdateCustomRoleReq, opts ...grpc.CallOption) (*UpdateCustomRoleResp, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateCustomRoleResp) + err := c.cc.Invoke(ctx, OrgUnitRole_UpdateCustomRole_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *orgUnitRoleClient) GetCustomRole(ctx context.Context, in *GetCustomRoleReq, opts ...grpc.CallOption) (*GetCustomRoleResp, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetCustomRoleResp) + err := c.cc.Invoke(ctx, OrgUnitRole_GetCustomRole_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *orgUnitRoleClient) DeleteCustomRole(ctx context.Context, in *DeleteCustomRoleReq, opts ...grpc.CallOption) (*DeleteCustomRoleResp, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DeleteCustomRoleResp) + err := c.cc.Invoke(ctx, OrgUnitRole_DeleteCustomRole_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // OrgUnitRoleServer is the server API for OrgUnitRole service. // All implementations must embed UnimplementedOrgUnitRoleServer // for forward compatibility. // // Service provided to manage org unit roles type OrgUnitRoleServer interface { - // Get List of available org unit roles, for specifc orgranisation + // Get List of available org unit roles (both built-in and custom), for specific organisation ListOrgUnitRoles(context.Context, *OrgUnitRolesListReq) (*OrgUnitRolesListResp, error) + // Create a new custom role for the organization unit + CreateCustomRole(context.Context, *CreateCustomRoleReq) (*CreateCustomRoleResp, error) + // Update an existing custom role for the organization unit + UpdateCustomRole(context.Context, *UpdateCustomRoleReq) (*UpdateCustomRoleResp, error) + // Get details of a specific custom role + GetCustomRole(context.Context, *GetCustomRoleReq) (*GetCustomRoleResp, error) + // Delete (soft delete) a custom role from the organization unit + DeleteCustomRole(context.Context, *DeleteCustomRoleReq) (*DeleteCustomRoleResp, error) mustEmbedUnimplementedOrgUnitRoleServer() } @@ -74,6 +134,18 @@ type UnimplementedOrgUnitRoleServer struct{} func (UnimplementedOrgUnitRoleServer) ListOrgUnitRoles(context.Context, *OrgUnitRolesListReq) (*OrgUnitRolesListResp, error) { return nil, status.Errorf(codes.Unimplemented, "method ListOrgUnitRoles not implemented") } +func (UnimplementedOrgUnitRoleServer) CreateCustomRole(context.Context, *CreateCustomRoleReq) (*CreateCustomRoleResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateCustomRole not implemented") +} +func (UnimplementedOrgUnitRoleServer) UpdateCustomRole(context.Context, *UpdateCustomRoleReq) (*UpdateCustomRoleResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateCustomRole not implemented") +} +func (UnimplementedOrgUnitRoleServer) GetCustomRole(context.Context, *GetCustomRoleReq) (*GetCustomRoleResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCustomRole not implemented") +} +func (UnimplementedOrgUnitRoleServer) DeleteCustomRole(context.Context, *DeleteCustomRoleReq) (*DeleteCustomRoleResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteCustomRole not implemented") +} func (UnimplementedOrgUnitRoleServer) mustEmbedUnimplementedOrgUnitRoleServer() {} func (UnimplementedOrgUnitRoleServer) testEmbeddedByValue() {} @@ -113,6 +185,78 @@ func _OrgUnitRole_ListOrgUnitRoles_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _OrgUnitRole_CreateCustomRole_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateCustomRoleReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(OrgUnitRoleServer).CreateCustomRole(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: OrgUnitRole_CreateCustomRole_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(OrgUnitRoleServer).CreateCustomRole(ctx, req.(*CreateCustomRoleReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _OrgUnitRole_UpdateCustomRole_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateCustomRoleReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(OrgUnitRoleServer).UpdateCustomRole(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: OrgUnitRole_UpdateCustomRole_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(OrgUnitRoleServer).UpdateCustomRole(ctx, req.(*UpdateCustomRoleReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _OrgUnitRole_GetCustomRole_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetCustomRoleReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(OrgUnitRoleServer).GetCustomRole(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: OrgUnitRole_GetCustomRole_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(OrgUnitRoleServer).GetCustomRole(ctx, req.(*GetCustomRoleReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _OrgUnitRole_DeleteCustomRole_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteCustomRoleReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(OrgUnitRoleServer).DeleteCustomRole(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: OrgUnitRole_DeleteCustomRole_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(OrgUnitRoleServer).DeleteCustomRole(ctx, req.(*DeleteCustomRoleReq)) + } + return interceptor(ctx, in, info, handler) +} + // OrgUnitRole_ServiceDesc is the grpc.ServiceDesc for OrgUnitRole service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -124,6 +268,22 @@ var OrgUnitRole_ServiceDesc = grpc.ServiceDesc{ MethodName: "ListOrgUnitRoles", Handler: _OrgUnitRole_ListOrgUnitRoles_Handler, }, + { + MethodName: "CreateCustomRole", + Handler: _OrgUnitRole_CreateCustomRole_Handler, + }, + { + MethodName: "UpdateCustomRole", + Handler: _OrgUnitRole_UpdateCustomRole_Handler, + }, + { + MethodName: "GetCustomRole", + Handler: _OrgUnitRole_GetCustomRole_Handler, + }, + { + MethodName: "DeleteCustomRole", + Handler: _OrgUnitRole_DeleteCustomRole_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "org-unit-role.proto", diff --git a/api/swagger/apidocs.swagger.json b/api/swagger/apidocs.swagger.json index d2af993..ce51d1f 100644 --- a/api/swagger/apidocs.swagger.json +++ b/api/swagger/apidocs.swagger.json @@ -220,9 +220,170 @@ ] } }, + "/api/auth/v1/ou/{ou}/role": { + "post": { + "summary": "Create a new custom role for the organization unit", + "operationId": "OrgUnitRole_CreateCustomRole", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/apiCreateCustomRoleResp" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "ou", + "description": "Organization unit ID", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/OrgUnitRoleCreateCustomRoleBody" + } + } + ], + "tags": [ + "OrgUnitRole" + ] + } + }, + "/api/auth/v1/ou/{ou}/role/{name}": { + "get": { + "summary": "Get details of a specific custom role", + "operationId": "OrgUnitRole_GetCustomRole", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/apiGetCustomRoleResp" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "ou", + "description": "Organization unit ID", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "name", + "description": "Name of the custom role to retrieve", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "OrgUnitRole" + ] + }, + "delete": { + "summary": "Delete (soft delete) a custom role from the organization unit", + "operationId": "OrgUnitRole_DeleteCustomRole", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/apiDeleteCustomRoleResp" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "ou", + "description": "Organization unit ID", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "name", + "description": "Name of the custom role to delete", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "OrgUnitRole" + ] + }, + "put": { + "summary": "Update an existing custom role for the organization unit", + "operationId": "OrgUnitRole_UpdateCustomRole", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/apiUpdateCustomRoleResp" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "ou", + "description": "Organization unit ID", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "name", + "description": "Name of the custom role to update", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/OrgUnitRoleUpdateCustomRoleBody" + } + } + ], + "tags": [ + "OrgUnitRole" + ] + } + }, "/api/auth/v1/ou/{ou}/roles": { "get": { - "summary": "Get List of available org unit roles, for specifc orgranisation", + "summary": "Get List of available org unit roles (both built-in and custom), for specific organisation", "operationId": "OrgUnitRole_ListOrgUnitRoles", "responses": { "200": { @@ -2343,6 +2504,54 @@ }, "title": "Identity provider update request" }, + "OrgUnitRoleCreateCustomRoleBody": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name of the custom role (must be unique within the org unit)" + }, + "displayName": { + "type": "string", + "title": "Display name for the custom role" + }, + "description": { + "type": "string", + "title": "Description explaining the purpose of this custom role" + }, + "permissions": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/apiRolePermission" + }, + "title": "List of permissions granted by this custom role" + } + }, + "title": "Create custom role request" + }, + "OrgUnitRoleUpdateCustomRoleBody": { + "type": "object", + "properties": { + "displayName": { + "type": "string", + "title": "Updated display name for the custom role" + }, + "description": { + "type": "string", + "title": "Updated description explaining the purpose of this custom role" + }, + "permissions": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/apiRolePermission" + }, + "title": "Updated list of permissions granted by this custom role" + } + }, + "title": "Update custom role request" + }, "OrgUnitUpdateOrgUnitBody": { "type": "object", "properties": { @@ -2494,6 +2703,16 @@ } } }, + "apiCreateCustomRoleResp": { + "type": "object", + "properties": { + "message": { + "type": "string", + "title": "Confirmation message" + } + }, + "title": "Create custom role response" + }, "apiCustomerAddReq": { "type": "object", "properties": { @@ -2601,6 +2820,60 @@ "apiDefaultOrgUnitResp": { "type": "object" }, + "apiDeleteCustomRoleResp": { + "type": "object", + "properties": { + "message": { + "type": "string", + "title": "Confirmation message" + } + }, + "title": "Delete custom role response" + }, + "apiGetCustomRoleResp": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name of the custom role" + }, + "displayName": { + "type": "string", + "title": "Display name for the custom role" + }, + "description": { + "type": "string", + "title": "Description explaining the purpose of this custom role" + }, + "permissions": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/apiRolePermission" + }, + "title": "List of permissions granted by this custom role" + }, + "created": { + "type": "string", + "format": "int64", + "title": "Timestamp when the role was created" + }, + "createdBy": { + "type": "string", + "title": "User who created this custom role" + }, + "updated": { + "type": "string", + "format": "int64", + "title": "Timestamp when the role was last updated" + }, + "updatedBy": { + "type": "string", + "title": "User who last updated this custom role" + } + }, + "title": "Get custom role response" + }, "apiGoogleIDPConfig": { "type": "object", "properties": { @@ -3144,6 +3417,23 @@ "desc": { "type": "string", "title": "role description, provding details about the role" + }, + "type": { + "type": "string", + "title": "role type: \"built-in\" for system roles (admin, auditor) or \"custom\" for user-defined roles" + }, + "displayName": { + "type": "string", + "title": "display name for custom roles (only applicable for custom roles)" + }, + "created": { + "type": "string", + "format": "int64", + "title": "creation timestamp for custom roles (only applicable for custom roles)" + }, + "createdBy": { + "type": "string", + "title": "user who created the role for custom roles (only applicable for custom roles)" } } }, @@ -3160,7 +3450,7 @@ } }, "description": "eventually if we start working with longer list of roles\n keep first index for count to provide pagination\n int32 count = 1;", - "title": "org unit roles list response" + "title": "org unit roles list response - includes both built-in and custom roles" }, "apiOrgUnitUpdateResp": { "type": "object" @@ -3314,6 +3604,45 @@ }, "title": "resource get response" }, + "apiResourceMatch": { + "type": "object", + "properties": { + "criteria": { + "type": "string", + "title": "Matching criteria type: \"exact\", \"prefix\", \"suffix\", \"regex\", \"wildcard\"\n- exact: Exact resource name match\n- prefix: Resource name starts with the key\n- suffix: Resource name ends with the key\n- regex: Resource name matches the regex pattern in key\n- wildcard: Key with * for wildcard matching (default if empty)" + }, + "key": { + "type": "string", + "title": "The matching key/pattern based on criteria\nFor wildcard: supports * (e.g., \"bucket-*\", \"*-prod\", \"*\")\nFor regex: valid regex pattern\nFor exact/prefix/suffix: literal string" + } + }, + "title": "Resource matching criteria for fine-grained permission control" + }, + "apiRolePermission": { + "type": "object", + "properties": { + "resource": { + "type": "string", + "title": "Resource name this permission applies to" + }, + "match": { + "$ref": "#/definitions/apiResourceMatch", + "title": "Resource matching criteria (optional, defaults to wildcard with key=\"*\")" + }, + "verbs": { + "type": "array", + "items": { + "type": "string" + }, + "title": "List of allowed verbs/actions for this resource (supports \"*\" for all verbs)" + }, + "action": { + "type": "string", + "title": "Action type: \"Allow\" or \"Deny\" (Deny takes precedence over Allow)" + } + }, + "title": "Permission definition for custom roles" + }, "apiTenantAdminCreateConfig": { "type": "object", "properties": { @@ -3751,6 +4080,16 @@ }, "title": "tenant list response" }, + "apiUpdateCustomRoleResp": { + "type": "object", + "properties": { + "message": { + "type": "string", + "title": "Confirmation message" + } + }, + "title": "Update custom role response" + }, "apiUserCreateReq": { "type": "object", "properties": { diff --git a/pkg/server/org-unit-role.go b/pkg/server/org-unit-role.go index a862b31..e948caf 100644 --- a/pkg/server/org-unit-role.go +++ b/pkg/server/org-unit-role.go @@ -7,7 +7,10 @@ import ( "context" "log" + auth "github.com/go-core-stack/auth/context" "github.com/go-core-stack/auth/route" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "github.com/go-core-stack/auth-gateway/api" "github.com/go-core-stack/auth-gateway/pkg/model" @@ -17,6 +20,8 @@ type OrgUnitRoleServer struct { api.UnimplementedOrgUnitRoleServer } +// Return only built-in roles (hardcoded) +// TODO: Custom roles will be added in a separate PR func (s *OrgUnitRoleServer) ListOrgUnitRoles(ctx context.Context, req *api.OrgUnitRolesListReq) (*api.OrgUnitRolesListResp, error) { log.Printf("received list request for org unit roles: %v", req) resp := &api.OrgUnitRolesListResp{ @@ -38,6 +43,54 @@ func (s *OrgUnitRoleServer) ListOrgUnitRoles(ctx context.Context, req *api.OrgUn return resp, nil } +// CreateCustomRole creates a new custom role for the org unit +// TODO: Implementation coming in a separate PR +func (s *OrgUnitRoleServer) CreateCustomRole(ctx context.Context, req *api.CreateCustomRoleReq) (*api.CreateCustomRoleResp, error) { + authInfo, _ := auth.GetAuthInfoFromContext(ctx) + if authInfo == nil { + return nil, status.Errorf(codes.Unauthenticated, "User not authenticated") + } + + // Stub implementation + return nil, status.Errorf(codes.Unimplemented, "Custom role creation not yet implemented") +} + +// UpdateCustomRole updates an existing custom role +// TODO: Implementation coming in a separate PR +func (s *OrgUnitRoleServer) UpdateCustomRole(ctx context.Context, req *api.UpdateCustomRoleReq) (*api.UpdateCustomRoleResp, error) { + authInfo, _ := auth.GetAuthInfoFromContext(ctx) + if authInfo == nil { + return nil, status.Errorf(codes.Unauthenticated, "User not authenticated") + } + + // Stub implementation + return nil, status.Errorf(codes.Unimplemented, "Custom role update not yet implemented") +} + +// GetCustomRole retrieves details of a specific custom role +// TODO: Implementation coming in a separate PR +func (s *OrgUnitRoleServer) GetCustomRole(ctx context.Context, req *api.GetCustomRoleReq) (*api.GetCustomRoleResp, error) { + authInfo, _ := auth.GetAuthInfoFromContext(ctx) + if authInfo == nil { + return nil, status.Errorf(codes.Unauthenticated, "User not authenticated") + } + + // Stub implementation + return nil, status.Errorf(codes.Unimplemented, "Custom role retrieval not yet implemented") +} + +// DeleteCustomRole deletes a custom role from the organization unit +// TODO: Implementation coming in a separate PR +func (s *OrgUnitRoleServer) DeleteCustomRole(ctx context.Context, req *api.DeleteCustomRoleReq) (*api.DeleteCustomRoleResp, error) { + authInfo, _ := auth.GetAuthInfoFromContext(ctx) + if authInfo == nil { + return nil, status.Errorf(codes.Unauthenticated, "User not authenticated") + } + + // Stub implementation + return nil, status.Errorf(codes.Unimplemented, "Custom role deletion not yet implemented") +} + func NewOrgUnitRoleServer(ctx *model.GrpcServerContext, ep string) *OrgUnitRoleServer { srv := &OrgUnitRoleServer{} api.RegisterOrgUnitRoleServer(ctx.Server, srv) From 4cc6b19101e00de26b70ac1f3a2e9b21e97c32f7 Mon Sep 17 00:00:00 2001 From: suryanshu185 Date: Thu, 23 Oct 2025 15:48:41 +0530 Subject: [PATCH 2/3] feat: Add org-unit scoped custom role management Implement comprehensive custom role management system for organization units with fine-grained permission-based access control (PBAC) and response filtering capabilities. Core Features: - Full CRUD operations for custom roles (Create, Read, Update, Delete) - Org-unit scoped roles with tenant isolation - Dynamic role validation for user assignments - Soft delete with binding checks (prevents deletion if users assigned) - Response filtering for list operations based on role permissions Signed-off-by: suryanshu185 --- api/org-unit-role.pb.go | 256 +++++++++++++----- api/org-unit-role.pb.route.go | 8 +- api/org-unit-role.proto | 49 +++- api/swagger/apidocs.swagger.json | 33 ++- main.go | 11 + pkg/gateway/routes.go | 22 +- pkg/gateway/server.go | 176 ++++++++++-- pkg/server/org-unit-role.go | 442 ++++++++++++++++++++++++++++--- pkg/server/org-unit-user.go | 50 +++- pkg/table/const.go | 3 + pkg/table/org-unit-role.go | 308 +++++++++++++++++++++ 11 files changed, 1207 insertions(+), 151 deletions(-) create mode 100644 pkg/table/org-unit-role.go diff --git a/api/org-unit-role.pb.go b/api/org-unit-role.pb.go index b0e84d4..3b3fe54 100644 --- a/api/org-unit-role.pb.go +++ b/api/org-unit-role.pb.go @@ -26,6 +26,124 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// Matching criteria types for resource-based permissions +type ResourceMatchCriteria int32 + +const ( + // Unspecified/default (treated as wildcard) + ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_UNSPECIFIED ResourceMatchCriteria = 0 + // Exact resource name match + ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_EXACT ResourceMatchCriteria = 1 + // Resource name starts with the key + ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_PREFIX ResourceMatchCriteria = 2 + // Resource name ends with the key + ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_SUFFIX ResourceMatchCriteria = 3 + // Resource name matches the regex pattern in key + ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_REGEX ResourceMatchCriteria = 4 + // Key uses wildcard matching with * (e.g., "bucket-*", "*-prod", "*") + ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_WILDCARD ResourceMatchCriteria = 5 +) + +// Enum value maps for ResourceMatchCriteria. +var ( + ResourceMatchCriteria_name = map[int32]string{ + 0: "RESOURCE_MATCH_CRITERIA_UNSPECIFIED", + 1: "RESOURCE_MATCH_CRITERIA_EXACT", + 2: "RESOURCE_MATCH_CRITERIA_PREFIX", + 3: "RESOURCE_MATCH_CRITERIA_SUFFIX", + 4: "RESOURCE_MATCH_CRITERIA_REGEX", + 5: "RESOURCE_MATCH_CRITERIA_WILDCARD", + } + ResourceMatchCriteria_value = map[string]int32{ + "RESOURCE_MATCH_CRITERIA_UNSPECIFIED": 0, + "RESOURCE_MATCH_CRITERIA_EXACT": 1, + "RESOURCE_MATCH_CRITERIA_PREFIX": 2, + "RESOURCE_MATCH_CRITERIA_SUFFIX": 3, + "RESOURCE_MATCH_CRITERIA_REGEX": 4, + "RESOURCE_MATCH_CRITERIA_WILDCARD": 5, + } +) + +func (x ResourceMatchCriteria) Enum() *ResourceMatchCriteria { + p := new(ResourceMatchCriteria) + *p = x + return p +} + +func (x ResourceMatchCriteria) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ResourceMatchCriteria) Descriptor() protoreflect.EnumDescriptor { + return file_org_unit_role_proto_enumTypes[0].Descriptor() +} + +func (ResourceMatchCriteria) Type() protoreflect.EnumType { + return &file_org_unit_role_proto_enumTypes[0] +} + +func (x ResourceMatchCriteria) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ResourceMatchCriteria.Descriptor instead. +func (ResourceMatchCriteria) EnumDescriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{0} +} + +// Action types for role permissions +type RolePermissionAction int32 + +const ( + // Unspecified action (invalid) + RolePermissionAction_ROLE_PERMISSION_ACTION_UNSPECIFIED RolePermissionAction = 0 + // Allow the specified verbs on the resource + RolePermissionAction_ROLE_PERMISSION_ACTION_ALLOW RolePermissionAction = 1 + // Deny the specified verbs on the resource (takes precedence over Allow) + RolePermissionAction_ROLE_PERMISSION_ACTION_DENY RolePermissionAction = 2 +) + +// Enum value maps for RolePermissionAction. +var ( + RolePermissionAction_name = map[int32]string{ + 0: "ROLE_PERMISSION_ACTION_UNSPECIFIED", + 1: "ROLE_PERMISSION_ACTION_ALLOW", + 2: "ROLE_PERMISSION_ACTION_DENY", + } + RolePermissionAction_value = map[string]int32{ + "ROLE_PERMISSION_ACTION_UNSPECIFIED": 0, + "ROLE_PERMISSION_ACTION_ALLOW": 1, + "ROLE_PERMISSION_ACTION_DENY": 2, + } +) + +func (x RolePermissionAction) Enum() *RolePermissionAction { + p := new(RolePermissionAction) + *p = x + return p +} + +func (x RolePermissionAction) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (RolePermissionAction) Descriptor() protoreflect.EnumDescriptor { + return file_org_unit_role_proto_enumTypes[1].Descriptor() +} + +func (RolePermissionAction) Type() protoreflect.EnumType { + return &file_org_unit_role_proto_enumTypes[1] +} + +func (x RolePermissionAction) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use RolePermissionAction.Descriptor instead. +func (RolePermissionAction) EnumDescriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{1} +} + // org unit roles list request type OrgUnitRolesListReq struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -211,13 +329,8 @@ func (x *OrgUnitRolesListResp) GetItems() []*OrgUnitRolesListEntry { // Resource matching criteria for fine-grained permission control type ResourceMatch struct { state protoimpl.MessageState `protogen:"open.v1"` - // Matching criteria type: "exact", "prefix", "suffix", "regex", "wildcard" - // - exact: Exact resource name match - // - prefix: Resource name starts with the key - // - suffix: Resource name ends with the key - // - regex: Resource name matches the regex pattern in key - // - wildcard: Key with * for wildcard matching (default if empty) - Criteria string `protobuf:"bytes,1,opt,name=criteria,proto3" json:"criteria,omitempty"` + // Matching criteria type + Criteria ResourceMatchCriteria `protobuf:"varint,1,opt,name=criteria,proto3,enum=api.ResourceMatchCriteria" json:"criteria,omitempty"` // The matching key/pattern based on criteria // For wildcard: supports * (e.g., "bucket-*", "*-prod", "*") // For regex: valid regex pattern @@ -257,11 +370,11 @@ func (*ResourceMatch) Descriptor() ([]byte, []int) { return file_org_unit_role_proto_rawDescGZIP(), []int{3} } -func (x *ResourceMatch) GetCriteria() string { +func (x *ResourceMatch) GetCriteria() ResourceMatchCriteria { if x != nil { return x.Criteria } - return "" + return ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_UNSPECIFIED } func (x *ResourceMatch) GetKey() string { @@ -280,8 +393,8 @@ type RolePermission struct { Match *ResourceMatch `protobuf:"bytes,2,opt,name=match,proto3" json:"match,omitempty"` // List of allowed verbs/actions for this resource (supports "*" for all verbs) Verbs []string `protobuf:"bytes,3,rep,name=verbs,proto3" json:"verbs,omitempty"` - // Action type: "Allow" or "Deny" (Deny takes precedence over Allow) - Action string `protobuf:"bytes,4,opt,name=action,proto3" json:"action,omitempty"` + // Action type: Allow or Deny (Deny takes precedence over Allow) + Action RolePermissionAction `protobuf:"varint,4,opt,name=action,proto3,enum=api.RolePermissionAction" json:"action,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -337,11 +450,11 @@ func (x *RolePermission) GetVerbs() []string { return nil } -func (x *RolePermission) GetAction() string { +func (x *RolePermission) GetAction() RolePermissionAction { if x != nil { return x.Action } - return "" + return RolePermissionAction_ROLE_PERMISSION_ACTION_UNSPECIFIED } // Create custom role request @@ -880,15 +993,15 @@ const file_org_unit_role_proto_rawDesc = "" + "\acreated\x18\x05 \x01(\x03R\acreated\x12\x1c\n" + "\tcreatedBy\x18\x06 \x01(\tR\tcreatedBy\"H\n" + "\x14OrgUnitRolesListResp\x120\n" + - "\x05items\x18\x02 \x03(\v2\x1a.api.OrgUnitRolesListEntryR\x05items\"=\n" + - "\rResourceMatch\x12\x1a\n" + - "\bcriteria\x18\x01 \x01(\tR\bcriteria\x12\x10\n" + - "\x03key\x18\x02 \x01(\tR\x03key\"\x84\x01\n" + + "\x05items\x18\x02 \x03(\v2\x1a.api.OrgUnitRolesListEntryR\x05items\"Y\n" + + "\rResourceMatch\x126\n" + + "\bcriteria\x18\x01 \x01(\x0e2\x1a.api.ResourceMatchCriteriaR\bcriteria\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\"\x9f\x01\n" + "\x0eRolePermission\x12\x1a\n" + "\bresource\x18\x01 \x01(\tR\bresource\x12(\n" + "\x05match\x18\x02 \x01(\v2\x12.api.ResourceMatchR\x05match\x12\x14\n" + - "\x05verbs\x18\x03 \x03(\tR\x05verbs\x12\x16\n" + - "\x06action\x18\x04 \x01(\tR\x06action\"\xb4\x01\n" + + "\x05verbs\x18\x03 \x03(\tR\x05verbs\x121\n" + + "\x06action\x18\x04 \x01(\x0e2\x19.api.RolePermissionActionR\x06action\"\xb4\x01\n" + "\x13CreateCustomRoleReq\x12\x0e\n" + "\x02ou\x18\x01 \x01(\tR\x02ou\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x12 \n" + @@ -921,18 +1034,29 @@ const file_org_unit_role_proto_rawDesc = "" + "\x02ou\x18\x01 \x01(\tR\x02ou\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\"0\n" + "\x14DeleteCustomRoleResp\x12\x18\n" + - "\amessage\x18\x01 \x01(\tR\amessage2\xf3\x05\n" + + "\amessage\x18\x01 \x01(\tR\amessage*\xf4\x01\n" + + "\x15ResourceMatchCriteria\x12'\n" + + "#RESOURCE_MATCH_CRITERIA_UNSPECIFIED\x10\x00\x12!\n" + + "\x1dRESOURCE_MATCH_CRITERIA_EXACT\x10\x01\x12\"\n" + + "\x1eRESOURCE_MATCH_CRITERIA_PREFIX\x10\x02\x12\"\n" + + "\x1eRESOURCE_MATCH_CRITERIA_SUFFIX\x10\x03\x12!\n" + + "\x1dRESOURCE_MATCH_CRITERIA_REGEX\x10\x04\x12$\n" + + " RESOURCE_MATCH_CRITERIA_WILDCARD\x10\x05*\x81\x01\n" + + "\x14RolePermissionAction\x12&\n" + + "\"ROLE_PERMISSION_ACTION_UNSPECIFIED\x10\x00\x12 \n" + + "\x1cROLE_PERMISSION_ACTION_ALLOW\x10\x01\x12\x1f\n" + + "\x1bROLE_PERMISSION_ACTION_DENY\x10\x022\xd7\x05\n" + "\vOrgUnitRole\x12\x88\x01\n" + "\x10ListOrgUnitRoles\x12\x18.api.OrgUnitRolesListReq\x1a\x19.api.OrgUnitRolesListResp\"?\x8a\xb5\x18\x19\n" + - "\rorg-unit-role\x12\x02ou\x1a\x04list\x82\xd3\xe4\x93\x02\x1c\x12\x1a/api/auth/v1/ou/{ou}/roles\x12\x93\x01\n" + - "\x10CreateCustomRole\x12\x18.api.CreateCustomRoleReq\x1a\x19.api.CreateCustomRoleResp\"J\x8a\xb5\x18\"\n" + - "\x14org-unit-custom-role\x12\x02ou\x1a\x06create\x82\xd3\xe4\x93\x02\x1e:\x01*\"\x19/api/auth/v1/ou/{ou}/role\x12\x9a\x01\n" + - "\x10UpdateCustomRole\x12\x18.api.UpdateCustomRoleReq\x1a\x19.api.UpdateCustomRoleResp\"Q\x8a\xb5\x18\"\n" + - "\x14org-unit-custom-role\x12\x02ou\x1a\x06update\x82\xd3\xe4\x93\x02%:\x01*\x1a /api/auth/v1/ou/{ou}/role/{name}\x12\x8b\x01\n" + - "\rGetCustomRole\x12\x15.api.GetCustomRoleReq\x1a\x16.api.GetCustomRoleResp\"K\x8a\xb5\x18\x1f\n" + - "\x14org-unit-custom-role\x12\x02ou\x1a\x03get\x82\xd3\xe4\x93\x02\"\x12 /api/auth/v1/ou/{ou}/role/{name}\x12\x97\x01\n" + - "\x10DeleteCustomRole\x12\x18.api.DeleteCustomRoleReq\x1a\x19.api.DeleteCustomRoleResp\"N\x8a\xb5\x18\"\n" + - "\x14org-unit-custom-role\x12\x02ou\x1a\x06delete\x82\xd3\xe4\x93\x02\"* /api/auth/v1/ou/{ou}/role/{name}B+Z)github.com/go-core-stack/auth-gateway/apib\x06proto3" + "\rorg-unit-role\x12\x02ou\x1a\x04list\x82\xd3\xe4\x93\x02\x1c\x12\x1a/api/auth/v1/ou/{ou}/roles\x12\x8c\x01\n" + + "\x10CreateCustomRole\x12\x18.api.CreateCustomRoleReq\x1a\x19.api.CreateCustomRoleResp\"C\x8a\xb5\x18\x1b\n" + + "\rorg-unit-role\x12\x02ou\x1a\x06create\x82\xd3\xe4\x93\x02\x1e:\x01*\"\x19/api/auth/v1/ou/{ou}/role\x12\x93\x01\n" + + "\x10UpdateCustomRole\x12\x18.api.UpdateCustomRoleReq\x1a\x19.api.UpdateCustomRoleResp\"J\x8a\xb5\x18\x1b\n" + + "\rorg-unit-role\x12\x02ou\x1a\x06update\x82\xd3\xe4\x93\x02%:\x01*\x1a /api/auth/v1/ou/{ou}/role/{name}\x12\x84\x01\n" + + "\rGetCustomRole\x12\x15.api.GetCustomRoleReq\x1a\x16.api.GetCustomRoleResp\"D\x8a\xb5\x18\x18\n" + + "\rorg-unit-role\x12\x02ou\x1a\x03get\x82\xd3\xe4\x93\x02\"\x12 /api/auth/v1/ou/{ou}/role/{name}\x12\x90\x01\n" + + "\x10DeleteCustomRole\x12\x18.api.DeleteCustomRoleReq\x1a\x19.api.DeleteCustomRoleResp\"G\x8a\xb5\x18\x1b\n" + + "\rorg-unit-role\x12\x02ou\x1a\x06delete\x82\xd3\xe4\x93\x02\"* /api/auth/v1/ou/{ou}/role/{name}B+Z)github.com/go-core-stack/auth-gateway/apib\x06proto3" var ( file_org_unit_role_proto_rawDescOnce sync.Once @@ -946,43 +1070,48 @@ func file_org_unit_role_proto_rawDescGZIP() []byte { return file_org_unit_role_proto_rawDescData } +var file_org_unit_role_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_org_unit_role_proto_msgTypes = make([]protoimpl.MessageInfo, 13) var file_org_unit_role_proto_goTypes = []any{ - (*OrgUnitRolesListReq)(nil), // 0: api.OrgUnitRolesListReq - (*OrgUnitRolesListEntry)(nil), // 1: api.OrgUnitRolesListEntry - (*OrgUnitRolesListResp)(nil), // 2: api.OrgUnitRolesListResp - (*ResourceMatch)(nil), // 3: api.ResourceMatch - (*RolePermission)(nil), // 4: api.RolePermission - (*CreateCustomRoleReq)(nil), // 5: api.CreateCustomRoleReq - (*CreateCustomRoleResp)(nil), // 6: api.CreateCustomRoleResp - (*UpdateCustomRoleReq)(nil), // 7: api.UpdateCustomRoleReq - (*UpdateCustomRoleResp)(nil), // 8: api.UpdateCustomRoleResp - (*GetCustomRoleReq)(nil), // 9: api.GetCustomRoleReq - (*GetCustomRoleResp)(nil), // 10: api.GetCustomRoleResp - (*DeleteCustomRoleReq)(nil), // 11: api.DeleteCustomRoleReq - (*DeleteCustomRoleResp)(nil), // 12: api.DeleteCustomRoleResp + (ResourceMatchCriteria)(0), // 0: api.ResourceMatchCriteria + (RolePermissionAction)(0), // 1: api.RolePermissionAction + (*OrgUnitRolesListReq)(nil), // 2: api.OrgUnitRolesListReq + (*OrgUnitRolesListEntry)(nil), // 3: api.OrgUnitRolesListEntry + (*OrgUnitRolesListResp)(nil), // 4: api.OrgUnitRolesListResp + (*ResourceMatch)(nil), // 5: api.ResourceMatch + (*RolePermission)(nil), // 6: api.RolePermission + (*CreateCustomRoleReq)(nil), // 7: api.CreateCustomRoleReq + (*CreateCustomRoleResp)(nil), // 8: api.CreateCustomRoleResp + (*UpdateCustomRoleReq)(nil), // 9: api.UpdateCustomRoleReq + (*UpdateCustomRoleResp)(nil), // 10: api.UpdateCustomRoleResp + (*GetCustomRoleReq)(nil), // 11: api.GetCustomRoleReq + (*GetCustomRoleResp)(nil), // 12: api.GetCustomRoleResp + (*DeleteCustomRoleReq)(nil), // 13: api.DeleteCustomRoleReq + (*DeleteCustomRoleResp)(nil), // 14: api.DeleteCustomRoleResp } var file_org_unit_role_proto_depIdxs = []int32{ - 1, // 0: api.OrgUnitRolesListResp.items:type_name -> api.OrgUnitRolesListEntry - 3, // 1: api.RolePermission.match:type_name -> api.ResourceMatch - 4, // 2: api.CreateCustomRoleReq.permissions:type_name -> api.RolePermission - 4, // 3: api.UpdateCustomRoleReq.permissions:type_name -> api.RolePermission - 4, // 4: api.GetCustomRoleResp.permissions:type_name -> api.RolePermission - 0, // 5: api.OrgUnitRole.ListOrgUnitRoles:input_type -> api.OrgUnitRolesListReq - 5, // 6: api.OrgUnitRole.CreateCustomRole:input_type -> api.CreateCustomRoleReq - 7, // 7: api.OrgUnitRole.UpdateCustomRole:input_type -> api.UpdateCustomRoleReq - 9, // 8: api.OrgUnitRole.GetCustomRole:input_type -> api.GetCustomRoleReq - 11, // 9: api.OrgUnitRole.DeleteCustomRole:input_type -> api.DeleteCustomRoleReq - 2, // 10: api.OrgUnitRole.ListOrgUnitRoles:output_type -> api.OrgUnitRolesListResp - 6, // 11: api.OrgUnitRole.CreateCustomRole:output_type -> api.CreateCustomRoleResp - 8, // 12: api.OrgUnitRole.UpdateCustomRole:output_type -> api.UpdateCustomRoleResp - 10, // 13: api.OrgUnitRole.GetCustomRole:output_type -> api.GetCustomRoleResp - 12, // 14: api.OrgUnitRole.DeleteCustomRole:output_type -> api.DeleteCustomRoleResp - 10, // [10:15] is the sub-list for method output_type - 5, // [5:10] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 3, // 0: api.OrgUnitRolesListResp.items:type_name -> api.OrgUnitRolesListEntry + 0, // 1: api.ResourceMatch.criteria:type_name -> api.ResourceMatchCriteria + 5, // 2: api.RolePermission.match:type_name -> api.ResourceMatch + 1, // 3: api.RolePermission.action:type_name -> api.RolePermissionAction + 6, // 4: api.CreateCustomRoleReq.permissions:type_name -> api.RolePermission + 6, // 5: api.UpdateCustomRoleReq.permissions:type_name -> api.RolePermission + 6, // 6: api.GetCustomRoleResp.permissions:type_name -> api.RolePermission + 2, // 7: api.OrgUnitRole.ListOrgUnitRoles:input_type -> api.OrgUnitRolesListReq + 7, // 8: api.OrgUnitRole.CreateCustomRole:input_type -> api.CreateCustomRoleReq + 9, // 9: api.OrgUnitRole.UpdateCustomRole:input_type -> api.UpdateCustomRoleReq + 11, // 10: api.OrgUnitRole.GetCustomRole:input_type -> api.GetCustomRoleReq + 13, // 11: api.OrgUnitRole.DeleteCustomRole:input_type -> api.DeleteCustomRoleReq + 4, // 12: api.OrgUnitRole.ListOrgUnitRoles:output_type -> api.OrgUnitRolesListResp + 8, // 13: api.OrgUnitRole.CreateCustomRole:output_type -> api.CreateCustomRoleResp + 10, // 14: api.OrgUnitRole.UpdateCustomRole:output_type -> api.UpdateCustomRoleResp + 12, // 15: api.OrgUnitRole.GetCustomRole:output_type -> api.GetCustomRoleResp + 14, // 16: api.OrgUnitRole.DeleteCustomRole:output_type -> api.DeleteCustomRoleResp + 12, // [12:17] is the sub-list for method output_type + 7, // [7:12] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_org_unit_role_proto_init() } @@ -995,13 +1124,14 @@ func file_org_unit_role_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_org_unit_role_proto_rawDesc), len(file_org_unit_role_proto_rawDesc)), - NumEnums: 0, + NumEnums: 2, NumMessages: 13, NumExtensions: 0, NumServices: 1, }, GoTypes: file_org_unit_role_proto_goTypes, DependencyIndexes: file_org_unit_role_proto_depIdxs, + EnumInfos: file_org_unit_role_proto_enumTypes, MessageInfos: file_org_unit_role_proto_msgTypes, }.Build() File_org_unit_role_proto = out.File diff --git a/api/org-unit-role.pb.route.go b/api/org-unit-role.pb.route.go index c7ced4c..0e01cbe 100644 --- a/api/org-unit-role.pb.route.go +++ b/api/org-unit-role.pb.route.go @@ -19,28 +19,28 @@ func init() { // Adding Route information for CreateCustomRole RPC route = model.NewRoute("/api/auth/v1/ou/{ou}/role", "POST") - route.Resource = "org-unit-custom-role" + route.Resource = "org-unit-role" route.Scopes = append(route.Scopes, "ou") route.Verb = "create" RoutesOrgUnitRole = append(RoutesOrgUnitRole, route) // Adding Route information for UpdateCustomRole RPC route = model.NewRoute("/api/auth/v1/ou/{ou}/role/{name}", "PUT") - route.Resource = "org-unit-custom-role" + route.Resource = "org-unit-role" route.Scopes = append(route.Scopes, "ou") route.Verb = "update" RoutesOrgUnitRole = append(RoutesOrgUnitRole, route) // Adding Route information for GetCustomRole RPC route = model.NewRoute("/api/auth/v1/ou/{ou}/role/{name}", "GET") - route.Resource = "org-unit-custom-role" + route.Resource = "org-unit-role" route.Scopes = append(route.Scopes, "ou") route.Verb = "get" RoutesOrgUnitRole = append(RoutesOrgUnitRole, route) // Adding Route information for DeleteCustomRole RPC route = model.NewRoute("/api/auth/v1/ou/{ou}/role/{name}", "DELETE") - route.Resource = "org-unit-custom-role" + route.Resource = "org-unit-role" route.Scopes = append(route.Scopes, "ou") route.Verb = "delete" RoutesOrgUnitRole = append(RoutesOrgUnitRole, route) diff --git a/api/org-unit-role.proto b/api/org-unit-role.proto index 023d6ab..cdcd972 100644 --- a/api/org-unit-role.proto +++ b/api/org-unit-role.proto @@ -31,7 +31,7 @@ service OrgUnitRole { body: "*" }; option (api.role) = { - resource: "org-unit-custom-role" + resource: "org-unit-role" scope: "ou" verb: "create" }; @@ -44,7 +44,7 @@ service OrgUnitRole { body: "*" }; option (api.role) = { - resource: "org-unit-custom-role" + resource: "org-unit-role" scope: "ou" verb: "update" }; @@ -56,7 +56,7 @@ service OrgUnitRole { get: "/api/auth/v1/ou/{ou}/role/{name}" }; option (api.role) = { - resource: "org-unit-custom-role" + resource: "org-unit-role" scope: "ou" verb: "get" }; @@ -68,7 +68,7 @@ service OrgUnitRole { delete: "/api/auth/v1/ou/{ou}/role/{name}" }; option (api.role) = { - resource: "org-unit-custom-role" + resource: "org-unit-role" scope: "ou" verb: "delete" }; @@ -111,15 +111,36 @@ message OrgUnitRolesListResp { repeated OrgUnitRolesListEntry items = 2; } +// Matching criteria types for resource-based permissions +enum ResourceMatchCriteria { + // Unspecified/default (treated as wildcard) + RESOURCE_MATCH_CRITERIA_UNSPECIFIED = 0; + // Exact resource name match + RESOURCE_MATCH_CRITERIA_EXACT = 1; + // Resource name starts with the key + RESOURCE_MATCH_CRITERIA_PREFIX = 2; + // Resource name ends with the key + RESOURCE_MATCH_CRITERIA_SUFFIX = 3; + // Resource name matches the regex pattern in key + RESOURCE_MATCH_CRITERIA_REGEX = 4; + // Key uses wildcard matching with * (e.g., "bucket-*", "*-prod", "*") + RESOURCE_MATCH_CRITERIA_WILDCARD = 5; +} + +// Action types for role permissions +enum RolePermissionAction { + // Unspecified action (invalid) + ROLE_PERMISSION_ACTION_UNSPECIFIED = 0; + // Allow the specified verbs on the resource + ROLE_PERMISSION_ACTION_ALLOW = 1; + // Deny the specified verbs on the resource (takes precedence over Allow) + ROLE_PERMISSION_ACTION_DENY = 2; +} + // Resource matching criteria for fine-grained permission control message ResourceMatch { - // Matching criteria type: "exact", "prefix", "suffix", "regex", "wildcard" - // - exact: Exact resource name match - // - prefix: Resource name starts with the key - // - suffix: Resource name ends with the key - // - regex: Resource name matches the regex pattern in key - // - wildcard: Key with * for wildcard matching (default if empty) - string criteria = 1; + // Matching criteria type + ResourceMatchCriteria criteria = 1; // The matching key/pattern based on criteria // For wildcard: supports * (e.g., "bucket-*", "*-prod", "*") @@ -139,8 +160,8 @@ message RolePermission { // List of allowed verbs/actions for this resource (supports "*" for all verbs) repeated string verbs = 3; - // Action type: "Allow" or "Deny" (Deny takes precedence over Allow) - string action = 4; + // Action type: Allow or Deny (Deny takes precedence over Allow) + RolePermissionAction action = 4; } // Create custom role request @@ -240,4 +261,4 @@ message DeleteCustomRoleReq { message DeleteCustomRoleResp { // Confirmation message string message = 1; -} +} \ No newline at end of file diff --git a/api/swagger/apidocs.swagger.json b/api/swagger/apidocs.swagger.json index ce51d1f..1a50dab 100644 --- a/api/swagger/apidocs.swagger.json +++ b/api/swagger/apidocs.swagger.json @@ -3608,8 +3608,8 @@ "type": "object", "properties": { "criteria": { - "type": "string", - "title": "Matching criteria type: \"exact\", \"prefix\", \"suffix\", \"regex\", \"wildcard\"\n- exact: Exact resource name match\n- prefix: Resource name starts with the key\n- suffix: Resource name ends with the key\n- regex: Resource name matches the regex pattern in key\n- wildcard: Key with * for wildcard matching (default if empty)" + "$ref": "#/definitions/apiResourceMatchCriteria", + "title": "Matching criteria type" }, "key": { "type": "string", @@ -3618,6 +3618,20 @@ }, "title": "Resource matching criteria for fine-grained permission control" }, + "apiResourceMatchCriteria": { + "type": "string", + "enum": [ + "RESOURCE_MATCH_CRITERIA_UNSPECIFIED", + "RESOURCE_MATCH_CRITERIA_EXACT", + "RESOURCE_MATCH_CRITERIA_PREFIX", + "RESOURCE_MATCH_CRITERIA_SUFFIX", + "RESOURCE_MATCH_CRITERIA_REGEX", + "RESOURCE_MATCH_CRITERIA_WILDCARD" + ], + "default": "RESOURCE_MATCH_CRITERIA_UNSPECIFIED", + "description": "- RESOURCE_MATCH_CRITERIA_UNSPECIFIED: Unspecified/default (treated as wildcard)\n - RESOURCE_MATCH_CRITERIA_EXACT: Exact resource name match\n - RESOURCE_MATCH_CRITERIA_PREFIX: Resource name starts with the key\n - RESOURCE_MATCH_CRITERIA_SUFFIX: Resource name ends with the key\n - RESOURCE_MATCH_CRITERIA_REGEX: Resource name matches the regex pattern in key\n - RESOURCE_MATCH_CRITERIA_WILDCARD: Key uses wildcard matching with * (e.g., \"bucket-*\", \"*-prod\", \"*\")", + "title": "Matching criteria types for resource-based permissions" + }, "apiRolePermission": { "type": "object", "properties": { @@ -3637,12 +3651,23 @@ "title": "List of allowed verbs/actions for this resource (supports \"*\" for all verbs)" }, "action": { - "type": "string", - "title": "Action type: \"Allow\" or \"Deny\" (Deny takes precedence over Allow)" + "$ref": "#/definitions/apiRolePermissionAction", + "title": "Action type: Allow or Deny (Deny takes precedence over Allow)" } }, "title": "Permission definition for custom roles" }, + "apiRolePermissionAction": { + "type": "string", + "enum": [ + "ROLE_PERMISSION_ACTION_UNSPECIFIED", + "ROLE_PERMISSION_ACTION_ALLOW", + "ROLE_PERMISSION_ACTION_DENY" + ], + "default": "ROLE_PERMISSION_ACTION_UNSPECIFIED", + "description": "- ROLE_PERMISSION_ACTION_UNSPECIFIED: Unspecified action (invalid)\n - ROLE_PERMISSION_ACTION_ALLOW: Allow the specified verbs on the resource\n - ROLE_PERMISSION_ACTION_DENY: Deny the specified verbs on the resource (takes precedence over Allow)", + "title": "Action types for role permissions" + }, "apiTenantAdminCreateConfig": { "type": "object", "properties": { diff --git a/main.go b/main.go index 1f17378..91c8051 100644 --- a/main.go +++ b/main.go @@ -434,6 +434,17 @@ func main() { log.Panicf("failed to start event logger for Org Unit User table: %s", err) } + // locate Org Unit Custom Role table + ouCustomRoleTbl, err := table.LocateOrgUnitCustomRoleTable(client) + if err != nil { + log.Panicf("failed to locate Org Unit Custom Role table: %s", err) + } + + err = ouCustomRoleTbl.StartEventLogger() + if err != nil { + log.Panicf("failed to start event logger for Org Unit Custom Role table: %s", err) + } + // ensure that the root tenant exists to work with as the default // tenancy locateRootTenant() diff --git a/pkg/gateway/routes.go b/pkg/gateway/routes.go index 35363e3..2e28f25 100644 --- a/pkg/gateway/routes.go +++ b/pkg/gateway/routes.go @@ -23,6 +23,8 @@ type routeData struct { isRoot bool isUserSpecific bool scopes []string + resource string + verb string } type routeNodes map[route.MethodType]routeData @@ -49,6 +51,8 @@ func populateRoutes(routes *route.RouteTable) { isRoot: utils.Dereference(r.IsRoot), isUserSpecific: utils.Dereference(r.IsUserSpecific), scopes: r.Scopes, + resource: r.Resource, + verb: r.Verb, }, } nRoutes.Insert(r.Key.Url, node) @@ -60,6 +64,8 @@ func populateRoutes(routes *route.RouteTable) { isRoot: utils.Dereference(r.IsRoot), isUserSpecific: utils.Dereference(r.IsUserSpecific), scopes: r.Scopes, + resource: r.Resource, + verb: r.Verb, } } } @@ -69,7 +75,7 @@ func populateRoutes(routes *route.RouteTable) { gwRoutes = nRoutes } -func matchRoute(m string, url string) (*routeData, string, error) { +func matchRoute(m string, url string) (*routeData, string, []string, []string, error) { var node *routeNodes var ok bool var keys, values []string @@ -82,7 +88,7 @@ func matchRoute(m string, url string) (*routeData, string, error) { }() if !ok { - return nil, "", errors.Wrapf(errors.NotFound, "route not found for %s", url) + return nil, "", nil, nil, errors.Wrapf(errors.NotFound, "route not found for %s", url) } var method route.MethodType @@ -106,19 +112,19 @@ func matchRoute(m string, url string) (*routeData, string, error) { case http.MethodTrace: method = route.TRACE default: - return nil, "", errors.Wrapf(errors.InvalidArgument, "invalid method %s", m) + return nil, "", nil, nil, errors.Wrapf(errors.InvalidArgument, "invalid method %s", m) } data, ok := (*node)[method] if !ok { - return nil, "", errors.Wrapf(errors.NotFound, "route not found for %s", url) + return nil, "", nil, nil, errors.Wrapf(errors.NotFound, "route not found for %s", url) } orgUnit := "" switch len(data.scopes) { case 1: if data.scopes[0] != "ou" { - return nil, "", errors.Wrapf(errors.InvalidArgument, "invalid scope %s for %s", data.scopes[0], url) + return nil, "", nil, nil, errors.Wrapf(errors.InvalidArgument, "invalid scope %s for %s", data.scopes[0], url) } for i, k := range keys { if k == "ou" { @@ -127,13 +133,13 @@ func matchRoute(m string, url string) (*routeData, string, error) { } } if orgUnit == "" { - return nil, "", errors.Wrapf(errors.InvalidArgument, "org unit not found") + return nil, "", nil, nil, errors.Wrapf(errors.InvalidArgument, "org unit not found") } case 0: break default: - return nil, "", errors.Wrapf(errors.InvalidArgument, "multiple scopes found for %s", url) + return nil, "", nil, nil, errors.Wrapf(errors.InvalidArgument, "multiple scopes found for %s", url) } - return &data, orgUnit, nil + return &data, orgUnit, keys, values, nil } diff --git a/pkg/gateway/server.go b/pkg/gateway/server.go index 8012794..7acfc61 100644 --- a/pkg/gateway/server.go +++ b/pkg/gateway/server.go @@ -12,6 +12,7 @@ import ( "net/http" "net/http/httputil" "os" + "regexp" "slices" "strings" "sync" @@ -44,14 +45,15 @@ var logger *zap.Logger type gateway struct { http.Handler - validator hash.Validator - apiKeys *table.ApiKeyTable - userTbl *table.UserTable - routes *route.RouteTable - ouTbl *table.OrgUnitTable - ouUserTbl *table.OrgUnitUserTable - proxyV1 *httputil.ReverseProxy - proxyV2 *httputil.ReverseProxy + validator hash.Validator + apiKeys *table.ApiKeyTable + userTbl *table.UserTable + routes *route.RouteTable + ouTbl *table.OrgUnitTable + ouUserTbl *table.OrgUnitUserTable + ouCustomRoleTbl *table.OrgUnitCustomRoleTable + proxyV1 *httputil.ReverseProxy + proxyV2 *httputil.ReverseProxy } type gatewayReconciler struct { @@ -206,7 +208,8 @@ func (s *gateway) AuthenticateRequest(r *http.Request) (*common.AuthInfo, error) // performOrgUnitRoleCheck checks if the Org unit role associated with the user // allows the requested access, returns true if the role allows access -func (s *gateway) performOrgUnitRoleCheck(authInfo *common.AuthInfo, ou string, r *http.Request) bool { +// For non-list operations, resourceInstance is the name/id of the specific resource being accessed +func (s *gateway) performOrgUnitRoleCheck(authInfo *common.AuthInfo, ou string, resource, verb, resourceInstance string, r *http.Request) bool { ouUserKey := &table.OrgUnitUserKey{ Tenant: authInfo.Realm, Username: authInfo.UserName, @@ -219,6 +222,8 @@ func (s *gateway) performOrgUnitRoleCheck(authInfo *common.AuthInfo, ou string, } return false } + + // Handle built-in roles switch ouUser.Role { case "admin": // wildcard access to the org unit @@ -230,7 +235,125 @@ func (s *gateway) performOrgUnitRoleCheck(authInfo *common.AuthInfo, ou string, } return false } - return false + + // Check custom role permissions + customRole, err := s.ouCustomRoleTbl.FindByNameAndOrgUnit(r.Context(), authInfo.Realm, ou, ouUser.Role) + if err != nil { + if !errors.IsNotFound(err) { + log.Printf("failed to find custom role %s for org unit %s: %s", ouUser.Role, ou, err) + } + return false + } + + // Evaluate permissions + return s.evaluateCustomRolePermissions(customRole.Permissions, resource, verb, resourceInstance) +} + +// evaluateCustomRolePermissions checks if the custom role's permissions allow the requested resource and verb +// For non-list operations, resourceInstance is checked against the permission's match criteria +func (s *gateway) evaluateCustomRolePermissions(permissions []*table.RolePermission, resource, verb, resourceInstance string) bool { + var allowMatched bool + var denyMatched bool + + for _, perm := range permissions { + // Check if resource matches + if !s.matchesResource(perm.Resource, perm.Match, resource) { + continue + } + + // Check if verb matches + verbMatches := false + for _, allowedVerb := range perm.Verbs { + if allowedVerb == "*" || allowedVerb == verb { + verbMatches = true + break + } + } + + if !verbMatches { + continue + } + + // For non-list operations, check if the resource instance matches the criteria + // List operations skip instance matching (show all, filter on individual access) + if verb != "list" && resourceInstance != "" { + if !s.matchesResourceInstance(resourceInstance, perm.Match) { + continue + } + } + + // Apply action (Deny takes precedence) + switch perm.Action { + case table.RolePermissionActionDeny: + denyMatched = true + case table.RolePermissionActionAllow, table.RolePermissionActionUnspecified: + // Treat UNSPECIFIED or empty as Allow (default permissive behavior) + allowMatched = true + } + } + + // Deny takes precedence over Allow + if denyMatched { + return false + } + + return allowMatched +} + +// matchesResource checks if a requested resource matches the permission's resource pattern +// This function performs resource type matching (e.g., "s3-object", "bucket") +func (s *gateway) matchesResource(permResource string, match *table.ResourceMatch, requestedResource string) bool { + // Handle wildcard resource matching (e.g., "*" matches all resources) + if permResource == "*" { + return true + } + + // Simple equality check - does the permission resource match the requested resource? + return permResource == requestedResource +} + +// matchesResourceInstance checks if a resource instance name matches the permission's match criteria +// Returns true if the instance matches the criteria, or if no criteria is specified +func (s *gateway) matchesResourceInstance(instanceName string, match *table.ResourceMatch) bool { + // If no match criteria specified, allow all instances of this resource type + if match == nil || match.Key == "" { + return true + } + + switch match.Criteria { + case table.ResourceMatchCriteriaExact: + return instanceName == match.Key + + case table.ResourceMatchCriteriaPrefix: + return strings.HasPrefix(instanceName, match.Key) + + case table.ResourceMatchCriteriaSuffix: + return strings.HasSuffix(instanceName, match.Key) + + case table.ResourceMatchCriteriaRegex: + matched, err := regexp.MatchString(match.Key, instanceName) + if err != nil { + log.Printf("Invalid regex pattern %s: %s", match.Key, err) + return false + } + return matched + + case table.ResourceMatchCriteriaWildcard, "": + // Default to wildcard matching + // Convert wildcard pattern to regex + pattern := strings.ReplaceAll(match.Key, "*", ".*") + pattern = "^" + pattern + "$" + matched, err := regexp.MatchString(pattern, instanceName) + if err != nil { + log.Printf("Invalid wildcard pattern %s: %s", match.Key, err) + return false + } + return matched + + default: + log.Printf("Unknown match criteria: %s", match.Criteria) + return false + } } func (s *gateway) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -242,13 +365,24 @@ func (s *gateway) ServeHTTP(w http.ResponseWriter, r *http.Request) { // RawPath will be an empty string path = r.URL.Path } - match, orgUnit, err := matchRoute(r.Method, path) + match, orgUnit, keys, values, err := matchRoute(r.Method, path) if err != nil { status = http.StatusNotFound http.Error(w, fmt.Sprintf("No route found for %s %s", r.Method, path), status) return } + // Extract resource instance name from URL path parameters + // Common patterns: /bucket/{name}, /s3-object/{name}, /user/{username} + resourceInstance := "" + for i, k := range keys { + // Look for common resource identifier keys + if k == "name" || k == "id" || k == "username" || k == "bucket" || k == "object" { + resourceInstance = values[i] + break + } + } + var authInfo *common.AuthInfo defer func() { if status != 0 { @@ -296,7 +430,7 @@ func (s *gateway) ServeHTTP(w http.ResponseWriter, r *http.Request) { if orgUnit != "" { // check if Org Unit Role associated with user, allows the // requested access - allow = s.performOrgUnitRoleCheck(authInfo, orgUnit, r) + allow = s.performOrgUnitRoleCheck(authInfo, orgUnit, match.resource, match.verb, resourceInstance, r) } if !allow { status = http.StatusForbidden @@ -466,6 +600,11 @@ func New() http.Handler { log.Panicf("unable to get org unit user table: %s", err) } + ouCustomRoleTbl, err := table.GetOrgUnitCustomRoleTable() + if err != nil { + log.Panicf("unable to get org unit custom role table: %s", err) + } + director := func(req *http.Request) { // we don't use director we will handle request modification // of our own @@ -481,12 +620,13 @@ func New() http.Handler { } gateway := &gateway{ - validator: hash.NewValidator(300), // Allow an API request to be valid for 5 mins, to handle offer if any - apiKeys: apiKeys, - userTbl: userTbl, - routes: routes, - ouTbl: ouTbl, - ouUserTbl: ouUserTbl, + validator: hash.NewValidator(300), // Allow an API request to be valid for 5 mins, to handle offer if any + apiKeys: apiKeys, + userTbl: userTbl, + routes: routes, + ouTbl: ouTbl, + ouUserTbl: ouUserTbl, + ouCustomRoleTbl: ouCustomRoleTbl, proxyV1: &httputil.ReverseProxy{ Director: director, Transport: tr1, diff --git a/pkg/server/org-unit-role.go b/pkg/server/org-unit-role.go index e948caf..bece257 100644 --- a/pkg/server/org-unit-role.go +++ b/pkg/server/org-unit-role.go @@ -5,96 +5,478 @@ package server import ( "context" + "fmt" "log" + "time" auth "github.com/go-core-stack/auth/context" "github.com/go-core-stack/auth/route" + "github.com/go-core-stack/core/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/go-core-stack/auth-gateway/api" "github.com/go-core-stack/auth-gateway/pkg/model" + "github.com/go-core-stack/auth-gateway/pkg/table" ) type OrgUnitRoleServer struct { api.UnimplementedOrgUnitRoleServer + customRoleTable *table.OrgUnitCustomRoleTable } -// Return only built-in roles (hardcoded) -// TODO: Custom roles will be added in a separate PR +// ListOrgUnitRoles returns both built-in and custom roles for an organization unit func (s *OrgUnitRoleServer) ListOrgUnitRoles(ctx context.Context, req *api.OrgUnitRolesListReq) (*api.OrgUnitRolesListResp, error) { log.Printf("received list request for org unit roles: %v", req) - resp := &api.OrgUnitRolesListResp{ - Items: []*api.OrgUnitRolesListEntry{ - { - Name: "default", - Desc: "Standard user role to provide access to all the resources available in the Organization Unit", - }, - { - Name: "admin", - Desc: "Administrator role to provide access to everything in the Organization Unit including management of users and resources", - }, - { - Name: "auditor", - Desc: "Auditor role to provider read-only access to all the resources available in the Organization Unit", - }, + + authInfo, _ := auth.GetAuthInfoFromContext(ctx) + if authInfo == nil { + return nil, status.Errorf(codes.Unauthenticated, "User not authenticated") + } + + items := []*api.OrgUnitRolesListEntry{ + { + Name: "admin", + Desc: "Administrator role to provide access to everything in the Organization Unit including management of users and resources", + Type: "built-in", + DisplayName: "Administrator", + Created: 0, + CreatedBy: "", + }, + { + Name: "auditor", + Desc: "Auditor role to provide read-only access to all the resources available in the Organization Unit", + Type: "built-in", + DisplayName: "Auditor", + Created: 0, + CreatedBy: "", }, } + + // Fetch custom roles from database + customRoles, err := s.customRoleTable.GetByOrgUnit(ctx, authInfo.Realm, req.Ou, 0, 1000) + if err != nil { + log.Printf("failed to fetch custom roles: %s", err) + } else { + for _, role := range customRoles { + items = append(items, &api.OrgUnitRolesListEntry{ + Name: role.Key.Name, + Desc: role.Description, + Type: "custom", + DisplayName: role.DisplayName, + Created: role.Created, + CreatedBy: role.CreatedBy, + }) + } + } + + resp := &api.OrgUnitRolesListResp{ + Items: items, + } return resp, nil } // CreateCustomRole creates a new custom role for the org unit -// TODO: Implementation coming in a separate PR func (s *OrgUnitRoleServer) CreateCustomRole(ctx context.Context, req *api.CreateCustomRoleReq) (*api.CreateCustomRoleResp, error) { authInfo, _ := auth.GetAuthInfoFromContext(ctx) if authInfo == nil { return nil, status.Errorf(codes.Unauthenticated, "User not authenticated") } - // Stub implementation - return nil, status.Errorf(codes.Unimplemented, "Custom role creation not yet implemented") + if err := s.validateCreateCustomRoleRequest(req); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Invalid request: %s", err) + } + + // Validate that role name is not one of the system reserved names + if req.Name == "admin" || req.Name == "default" || req.Name == "auditor" { + return nil, status.Errorf(codes.InvalidArgument, "Role name '%s' is reserved and cannot be used for custom roles", req.Name) + } + + // Check if there's an existing role (including soft-deleted ones) with the same name + existingRole, err := s.customRoleTable.FindAnyByNameAndOrgUnit(ctx, authInfo.Realm, req.Ou, req.Name) + if err == nil { + // Role exists - check if it's active or soft-deleted with bindings + if existingRole.Active == nil || *existingRole.Active { + return nil, status.Errorf(codes.AlreadyExists, "Custom role '%s' already exists in organization unit", req.Name) + } + + // Soft-deleted role exists - check if it has bindings + hasBindings, err := s.customRoleTable.HasBindings(ctx, authInfo.Realm, req.Ou, req.Name) + if err != nil { + log.Printf("failed to check bindings for role %s: %s", req.Name, err) + return nil, status.Errorf(codes.Internal, "Something went wrong, please try again later") + } + + if hasBindings { + return nil, status.Errorf(codes.AlreadyExists, "Custom role '%s' already exists in organization unit", req.Name) + } + + // Soft-deleted role without bindings - can be permanently removed and recreated + key := &table.OrgUnitCustomRoleKey{ + Tenant: authInfo.Realm, + OrgUnitId: req.Ou, + Name: req.Name, + } + err = s.customRoleTable.PermanentDelete(ctx, key) + if err != nil { + log.Printf("failed to permanently delete orphaned role %s: %s", req.Name, err) + return nil, status.Errorf(codes.Internal, "Something went wrong, please try again later") + } + } else if !errors.IsNotFound(err) { + log.Printf("failed to check existing role %s: %s", req.Name, err) + return nil, status.Errorf(codes.Internal, "Something went wrong, please try again later") + } + + // Convert protobuf permissions to table permissions + permissions := s.convertProtoPermissionsToTable(req.Permissions) + + // Create the custom role entry + customRole := &table.OrgUnitCustomRole{ + Key: &table.OrgUnitCustomRoleKey{ + Tenant: authInfo.Realm, + OrgUnitId: req.Ou, + Name: req.Name, + }, + DisplayName: req.DisplayName, + Description: req.Description, + Permissions: permissions, + Created: time.Now().Unix(), + CreatedBy: authInfo.UserName, + Active: &[]bool{true}[0], + } + + // Insert the custom role into the database + err = s.customRoleTable.Insert(ctx, customRole.Key, customRole) + if err != nil { + if errors.IsAlreadyExists(err) { + return nil, status.Errorf(codes.AlreadyExists, "Custom role '%s' already exists in organization unit", req.Name) + } + log.Printf("failed to create custom role: %s", err) + return nil, status.Errorf(codes.Internal, "Something went wrong, please try again later") + } + + return &api.CreateCustomRoleResp{ + Message: "Custom role created successfully", + }, nil } // UpdateCustomRole updates an existing custom role -// TODO: Implementation coming in a separate PR func (s *OrgUnitRoleServer) UpdateCustomRole(ctx context.Context, req *api.UpdateCustomRoleReq) (*api.UpdateCustomRoleResp, error) { authInfo, _ := auth.GetAuthInfoFromContext(ctx) if authInfo == nil { return nil, status.Errorf(codes.Unauthenticated, "User not authenticated") } - // Stub implementation - return nil, status.Errorf(codes.Unimplemented, "Custom role update not yet implemented") + if err := s.validateUpdateCustomRoleRequest(req); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Invalid request: %s", err) + } + + permissions := s.convertProtoPermissionsToTable(req.Permissions) + + updateRole := &table.OrgUnitCustomRole{ + DisplayName: req.DisplayName, + Description: req.Description, + Permissions: permissions, + Updated: time.Now().Unix(), + UpdatedBy: authInfo.UserName, + } + + key := &table.OrgUnitCustomRoleKey{ + Tenant: authInfo.Realm, + OrgUnitId: req.Ou, + Name: req.Name, + } + + err := s.customRoleTable.Update(ctx, key, updateRole) + if err != nil { + if errors.IsNotFound(err) { + return nil, status.Errorf(codes.NotFound, "Custom role '%s' not found in organization unit", req.Name) + } + log.Printf("failed to update custom role: %s", err) + return nil, status.Errorf(codes.Internal, "Something went wrong, please try again later") + } + + return &api.UpdateCustomRoleResp{ + Message: "Custom role updated successfully", + }, nil } // GetCustomRole retrieves details of a specific custom role -// TODO: Implementation coming in a separate PR func (s *OrgUnitRoleServer) GetCustomRole(ctx context.Context, req *api.GetCustomRoleReq) (*api.GetCustomRoleResp, error) { authInfo, _ := auth.GetAuthInfoFromContext(ctx) if authInfo == nil { return nil, status.Errorf(codes.Unauthenticated, "User not authenticated") } - // Stub implementation - return nil, status.Errorf(codes.Unimplemented, "Custom role retrieval not yet implemented") + customRole, err := s.customRoleTable.FindByNameAndOrgUnit(ctx, authInfo.Realm, req.Ou, req.Name) + if err != nil { + if errors.IsNotFound(err) { + return nil, status.Errorf(codes.NotFound, "Custom role '%s' not found in organization unit", req.Name) + } + log.Printf("failed to get custom role: %s", err) + return nil, status.Errorf(codes.Internal, "Something went wrong, please try again later") + } + + permissions := s.convertTablePermissionsToProto(customRole.Permissions) + + return &api.GetCustomRoleResp{ + Name: customRole.Key.Name, + DisplayName: customRole.DisplayName, + Description: customRole.Description, + Permissions: permissions, + Created: customRole.Created, + CreatedBy: customRole.CreatedBy, + Updated: customRole.Updated, + UpdatedBy: customRole.UpdatedBy, + }, nil } // DeleteCustomRole deletes a custom role from the organization unit -// TODO: Implementation coming in a separate PR func (s *OrgUnitRoleServer) DeleteCustomRole(ctx context.Context, req *api.DeleteCustomRoleReq) (*api.DeleteCustomRoleResp, error) { authInfo, _ := auth.GetAuthInfoFromContext(ctx) if authInfo == nil { return nil, status.Errorf(codes.Unauthenticated, "User not authenticated") } - // Stub implementation - return nil, status.Errorf(codes.Unimplemented, "Custom role deletion not yet implemented") + key := &table.OrgUnitCustomRoleKey{ + Tenant: authInfo.Realm, + OrgUnitId: req.Ou, + Name: req.Name, + } + + err := s.customRoleTable.DeleteCustomRoleWithBindingCheck(ctx, key, authInfo.UserName) + if err != nil { + if errors.IsNotFound(err) { + return nil, status.Errorf(codes.NotFound, "Custom role '%s' not found in organization unit", req.Name) + } + log.Printf("failed to delete custom role: %s", err) + return nil, status.Errorf(codes.Internal, "Something went wrong, please try again later") + } + + return &api.DeleteCustomRoleResp{ + Message: "Custom role deleted successfully", + }, nil +} + +// convertProtoResourceMatchCriteriaToTable converts protobuf enum to table string constant +func (s *OrgUnitRoleServer) convertProtoResourceMatchCriteriaToTable(criteria api.ResourceMatchCriteria) table.ResourceMatchCriteria { + switch criteria { + case api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_EXACT: + return table.ResourceMatchCriteriaExact + case api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_PREFIX: + return table.ResourceMatchCriteriaPrefix + case api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_SUFFIX: + return table.ResourceMatchCriteriaSuffix + case api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_REGEX: + return table.ResourceMatchCriteriaRegex + case api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_WILDCARD: + return table.ResourceMatchCriteriaWildcard + case api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_UNSPECIFIED: + fallthrough + default: + return table.ResourceMatchCriteriaUnspecified + } +} + +// convertTableResourceMatchCriteriaToProto converts table string constant to protobuf enum +func (s *OrgUnitRoleServer) convertTableResourceMatchCriteriaToProto(criteria table.ResourceMatchCriteria) api.ResourceMatchCriteria { + switch criteria { + case table.ResourceMatchCriteriaExact: + return api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_EXACT + case table.ResourceMatchCriteriaPrefix: + return api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_PREFIX + case table.ResourceMatchCriteriaSuffix: + return api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_SUFFIX + case table.ResourceMatchCriteriaRegex: + return api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_REGEX + case table.ResourceMatchCriteriaWildcard: + return api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_WILDCARD + case table.ResourceMatchCriteriaUnspecified: + fallthrough + default: + return api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_UNSPECIFIED + } +} + +// convertProtoActionToTable converts protobuf enum to table string constant +func (s *OrgUnitRoleServer) convertProtoActionToTable(action api.RolePermissionAction) table.RolePermissionAction { + switch action { + case api.RolePermissionAction_ROLE_PERMISSION_ACTION_ALLOW: + return table.RolePermissionActionAllow + case api.RolePermissionAction_ROLE_PERMISSION_ACTION_DENY: + return table.RolePermissionActionDeny + case api.RolePermissionAction_ROLE_PERMISSION_ACTION_UNSPECIFIED: + fallthrough + default: + return table.RolePermissionActionUnspecified + } +} + +// convertTableActionToProto converts table string constant to protobuf enum +func (s *OrgUnitRoleServer) convertTableActionToProto(action table.RolePermissionAction) api.RolePermissionAction { + switch action { + case table.RolePermissionActionAllow: + return api.RolePermissionAction_ROLE_PERMISSION_ACTION_ALLOW + case table.RolePermissionActionDeny: + return api.RolePermissionAction_ROLE_PERMISSION_ACTION_DENY + case table.RolePermissionActionUnspecified: + fallthrough + default: + return api.RolePermissionAction_ROLE_PERMISSION_ACTION_UNSPECIFIED + } +} + +// convertProtoPermissionsToTable converts protobuf RolePermission slice to table RolePermission slice +func (s *OrgUnitRoleServer) convertProtoPermissionsToTable(protoPerms []*api.RolePermission) []*table.RolePermission { + var permissions []*table.RolePermission + for _, perm := range protoPerms { + var match *table.ResourceMatch + if perm.Match != nil { + match = &table.ResourceMatch{ + Criteria: s.convertProtoResourceMatchCriteriaToTable(perm.Match.Criteria), + Key: perm.Match.Key, + } + } + + permissions = append(permissions, &table.RolePermission{ + Resource: perm.Resource, + Match: match, + Verbs: perm.Verbs, + Action: s.convertProtoActionToTable(perm.Action), + }) + } + return permissions +} + +// convertTablePermissionsToProto converts table RolePermission slice to protobuf RolePermission slice +func (s *OrgUnitRoleServer) convertTablePermissionsToProto(tablePerms []*table.RolePermission) []*api.RolePermission { + var permissions []*api.RolePermission + for _, perm := range tablePerms { + var match *api.ResourceMatch + if perm.Match != nil { + match = &api.ResourceMatch{ + Criteria: s.convertTableResourceMatchCriteriaToProto(perm.Match.Criteria), + Key: perm.Match.Key, + } + } + + permissions = append(permissions, &api.RolePermission{ + Resource: perm.Resource, + Match: match, + Verbs: perm.Verbs, + Action: s.convertTableActionToProto(perm.Action), + }) + } + return permissions +} + +// validateCreateCustomRoleRequest validates the CreateCustomRole request fields +func (s *OrgUnitRoleServer) validateCreateCustomRoleRequest(req *api.CreateCustomRoleReq) error { + if req.Name == "" { + return errors.New("role name cannot be empty") + } + if len(req.Name) < 2 { + return errors.New("role name must be at least 2 characters long") + } + if len(req.Name) > 50 { + return errors.New("role name cannot exceed 50 characters") + } + + if req.DisplayName == "" { + return errors.New("display name cannot be empty") + } + if len(req.DisplayName) > 100 { + return errors.New("display name cannot exceed 100 characters") + } + + // Description is optional, but if provided, validate length + if len(req.Description) > 500 { + return errors.New("description cannot exceed 500 characters") + } + + if len(req.Permissions) == 0 { + return errors.New("at least one permission must be specified") + } + if len(req.Permissions) > 20 { + return errors.New("cannot have more than 20 permissions per role") + } + + for i, perm := range req.Permissions { + if perm.Resource == "" { + return fmt.Errorf("permission %d: resource cannot be empty", i+1) + } + if len(perm.Verbs) == 0 { + return fmt.Errorf("permission %d: at least one verb must be specified", i+1) + } + for j, verb := range perm.Verbs { + if verb == "" { + return fmt.Errorf("permission %d, verb %d: verb cannot be empty", i+1, j+1) + } + } + if perm.Action != api.RolePermissionAction_ROLE_PERMISSION_ACTION_UNSPECIFIED && + perm.Action != api.RolePermissionAction_ROLE_PERMISSION_ACTION_ALLOW && + perm.Action != api.RolePermissionAction_ROLE_PERMISSION_ACTION_DENY { + return fmt.Errorf("permission %d: invalid action value: %v", i+1, perm.Action) + } + } + + return nil +} + +// validateUpdateCustomRoleRequest validates the UpdateCustomRole request fields +func (s *OrgUnitRoleServer) validateUpdateCustomRoleRequest(req *api.UpdateCustomRoleReq) error { + if req.DisplayName == "" { + return errors.New("display name cannot be empty") + } + if len(req.DisplayName) > 100 { + return errors.New("display name cannot exceed 100 characters") + } + + // Description is optional, but if provided, validate length + if len(req.Description) > 500 { + return errors.New("description cannot exceed 500 characters") + } + + if len(req.Permissions) == 0 { + return errors.New("at least one permission must be specified") + } + if len(req.Permissions) > 20 { + return errors.New("cannot have more than 20 permissions per role") + } + + for i, perm := range req.Permissions { + if perm.Resource == "" { + return fmt.Errorf("permission %d: resource cannot be empty", i+1) + } + if len(perm.Verbs) == 0 { + return fmt.Errorf("permission %d: at least one verb must be specified", i+1) + } + for j, verb := range perm.Verbs { + if verb == "" { + return fmt.Errorf("permission %d, verb %d: verb cannot be empty", i+1, j+1) + } + } + if perm.Action != api.RolePermissionAction_ROLE_PERMISSION_ACTION_UNSPECIFIED && + perm.Action != api.RolePermissionAction_ROLE_PERMISSION_ACTION_ALLOW && + perm.Action != api.RolePermissionAction_ROLE_PERMISSION_ACTION_DENY { + return fmt.Errorf("permission %d: invalid action value: %v", i+1, perm.Action) + } + } + + return nil } func NewOrgUnitRoleServer(ctx *model.GrpcServerContext, ep string) *OrgUnitRoleServer { - srv := &OrgUnitRoleServer{} + customRoleTable, err := table.GetOrgUnitCustomRoleTable() + if err != nil { + log.Panicf("failed to get org unit custom role table: %s", err) + } + + srv := &OrgUnitRoleServer{ + customRoleTable: customRoleTable, + } api.RegisterOrgUnitRoleServer(ctx.Server, srv) - err := api.RegisterOrgUnitRoleHandler(context.Background(), ctx.Mux, ctx.Conn) + err = api.RegisterOrgUnitRoleHandler(context.Background(), ctx.Mux, ctx.Conn) if err != nil { log.Panicf("failed to register handler: %s", err) } diff --git a/pkg/server/org-unit-user.go b/pkg/server/org-unit-user.go index b9795a5..75248e0 100644 --- a/pkg/server/org-unit-user.go +++ b/pkg/server/org-unit-user.go @@ -22,7 +22,28 @@ import ( type OrgUnitUserServer struct { api.UnimplementedOrgUnitUserServer - tbl *table.OrgUnitUserTable + tbl *table.OrgUnitUserTable + customRoleTable *table.OrgUnitCustomRoleTable +} + +// isValidRole checks if a role is valid (either built-in or custom) +func (s *OrgUnitUserServer) isValidRole(ctx context.Context, tenant, orgUnitId, role string) error { + // Check if it's a built-in role + if role == "admin" || role == "auditor" { + return nil + } + + // Check if it's a valid custom role + _, err := s.customRoleTable.FindByNameAndOrgUnit(ctx, tenant, orgUnitId, role) + if err != nil { + if errors.IsNotFound(err) { + return status.Errorf(codes.InvalidArgument, "Invalid role: '%s'. Must be 'admin', 'auditor', or a valid custom role", role) + } + log.Printf("failed to check custom role existence: %s", err) + return status.Errorf(codes.Internal, "Failed to validate role") + } + + return nil } func (s *OrgUnitUserServer) ListOrgUnitUsers(ctx context.Context, req *api.OrgUnitUsersListReq) (*api.OrgUnitUsersListResp, error) { @@ -62,9 +83,10 @@ func (s *OrgUnitUserServer) AddOrgUnitUser(ctx context.Context, req *api.OrgUnit return nil, status.Errorf(codes.Unauthenticated, "User not authenticated") } - // validate role, currently only admin, default and auditor roles are allowed - if req.Role != "admin" && req.Role != "default" && req.Role != "auditor" { - return nil, status.Errorf(codes.InvalidArgument, "Invalid role: %s", req.Role) + // Validate role (built-in or custom) + err := s.isValidRole(ctx, authInfo.Realm, req.Ou, req.Role) + if err != nil { + return nil, err } // TODO: validate if user exists, this might be never done to allow adding users @@ -81,7 +103,7 @@ func (s *OrgUnitUserServer) AddOrgUnitUser(ctx context.Context, req *api.OrgUnit Role: req.Role, } - err := s.tbl.Insert(ctx, entry.Key, entry) + err = s.tbl.Insert(ctx, entry.Key, entry) if err != nil { if errors.IsAlreadyExists(err) { return nil, status.Errorf(codes.AlreadyExists, "Org Unit User %s, already exists", req.User) @@ -99,9 +121,10 @@ func (s *OrgUnitUserServer) UpdateOrgUnitUser(ctx context.Context, req *api.OrgU return nil, status.Errorf(codes.Unauthenticated, "User not authenticated") } - // validate role, currently only admin, default and auditor roles are allowed - if req.Role != "admin" && req.Role != "default" && req.Role != "auditor" { - return nil, status.Errorf(codes.InvalidArgument, "Invalid role: %s", req.Role) + // Validate role (built-in or custom) + err := s.isValidRole(ctx, authInfo.Realm, req.Ou, req.Role) + if err != nil { + return nil, err } update := &table.OrgUnitUser{ @@ -113,7 +136,7 @@ func (s *OrgUnitUserServer) UpdateOrgUnitUser(ctx context.Context, req *api.OrgU Role: req.Role, } - err := s.tbl.Update(ctx, update.Key, update) + err = s.tbl.Update(ctx, update.Key, update) if err != nil { if errors.IsNotFound(err) { return nil, status.Errorf(codes.NotFound, "Org Unit User %s, not found", req.User) @@ -153,8 +176,15 @@ func NewOrgUnitUserServer(ctx *model.GrpcServerContext, ep string) *OrgUnitUserS if err != nil { log.Panicf("failed to get org unit user table: %s", err) } + + customRoleTable, err := table.GetOrgUnitCustomRoleTable() + if err != nil { + log.Panicf("failed to get org unit custom role table: %s", err) + } + srv := &OrgUnitUserServer{ - tbl: tbl, + tbl: tbl, + customRoleTable: customRoleTable, } api.RegisterOrgUnitUserServer(ctx.Server, srv) err = api.RegisterOrgUnitUserHandler(context.Background(), ctx.Mux, ctx.Conn) diff --git a/pkg/table/const.go b/pkg/table/const.go index a06406f..0aa1a0f 100644 --- a/pkg/table/const.go +++ b/pkg/table/const.go @@ -30,6 +30,9 @@ const ( // Org Unit User collection name OrgUnitUserCollectionName = "org-unit-users" + // Org Unit Custom Role collection name + OrgUnitCustomRoleCollectionName = "org-unit-roles" + // Identity Provider collection name IdentityProviderCollectionName = "identity-providers" ) diff --git a/pkg/table/org-unit-role.go b/pkg/table/org-unit-role.go new file mode 100644 index 0000000..9084f65 --- /dev/null +++ b/pkg/table/org-unit-role.go @@ -0,0 +1,308 @@ +// Copyright © 2025 Prabhjot Singh Sethi, All Rights reserved +// Author: Suryanshu Gupta + +package table + +import ( + "context" + "time" + + "github.com/go-core-stack/core/db" + "github.com/go-core-stack/core/errors" + "github.com/go-core-stack/core/table" + "go.mongodb.org/mongo-driver/bson" +) + +var orgUnitCustomRoleTable *OrgUnitCustomRoleTable + +// OrgUnitCustomRoleKey defines the key structure for custom roles +type OrgUnitCustomRoleKey struct { + // Tenant name this custom role belongs to + Tenant string `bson:"tenant,omitempty"` + // Org unit ID this custom role is scoped to + OrgUnitId string `bson:"orgUnitId,omitempty"` + // Role name, unique within the org unit + Name string `bson:"name,omitempty"` +} + +// ResourceMatchCriteria defines allowed values for resource matching criteria +type ResourceMatchCriteria string + +const ( + ResourceMatchCriteriaUnspecified ResourceMatchCriteria = "" + ResourceMatchCriteriaExact ResourceMatchCriteria = "exact" + ResourceMatchCriteriaPrefix ResourceMatchCriteria = "prefix" + ResourceMatchCriteriaSuffix ResourceMatchCriteria = "suffix" + ResourceMatchCriteriaRegex ResourceMatchCriteria = "regex" + ResourceMatchCriteriaWildcard ResourceMatchCriteria = "wildcard" +) + +// ResourceMatch defines matching criteria for resource-based permissions +type ResourceMatch struct { + // Matching criteria type + Criteria ResourceMatchCriteria `bson:"criteria,omitempty"` + // The matching key/pattern based on criteria + Key string `bson:"key,omitempty"` +} + +// RolePermissionAction defines allowed values for role permission actions +type RolePermissionAction string + +const ( + RolePermissionActionUnspecified RolePermissionAction = "" + RolePermissionActionAllow RolePermissionAction = "Allow" + RolePermissionActionDeny RolePermissionAction = "Deny" +) + +// RolePermission defines individual permission for a resource +type RolePermission struct { + // Resource name the permission applies to + Resource string `bson:"resource,omitempty"` + // Resource matching criteria (optional, defaults to wildcard with key="*") + Match *ResourceMatch `bson:"match,omitempty"` + // List of allowed verbs/actions for this resource (supports "*" for all verbs) + Verbs []string `bson:"verbs,omitempty"` + // Action type: Allow or Deny (Deny takes precedence over Allow) + Action RolePermissionAction `bson:"action,omitempty"` +} + +// OrgUnitCustomRole defines a custom role within an organization unit +type OrgUnitCustomRole struct { + // Custom role key + Key *OrgUnitCustomRoleKey `bson:"key,omitempty"` + // Display name for the custom role + DisplayName string `bson:"displayName,omitempty"` + // Description explaining the purpose of this custom role + Description string `bson:"description,omitempty"` + // List of permissions granted by this custom role + Permissions []*RolePermission `bson:"permissions,omitempty"` + // Created timestamp + Created int64 `bson:"created,omitempty"` + // User who created this custom role + CreatedBy string `bson:"createdBy,omitempty"` + // Last updated timestamp + Updated int64 `bson:"updated,omitempty"` + // User who last updated this custom role + UpdatedBy string `bson:"updatedBy,omitempty"` + // Whether this custom role is currently active + Active *bool `bson:"active,omitempty"` +} + +// OrgUnitCustomRoleTable manages custom roles for organization units +type OrgUnitCustomRoleTable struct { + table.Table[OrgUnitCustomRoleKey, OrgUnitCustomRole] + col db.StoreCollection +} + +// GetByOrgUnit retrieves all custom roles for a specific organization unit +func (t *OrgUnitCustomRoleTable) GetByOrgUnit(ctx context.Context, tenant, orgUnitId string, offset, limit int32) ([]*OrgUnitCustomRole, error) { + filter := bson.M{ + "key.tenant": tenant, + "key.orgUnitId": orgUnitId, + "active": bson.M{"$ne": false}, // Include roles where active is true or nil + } + + list, err := t.FindMany(ctx, filter, offset, limit) + if err != nil { + return nil, err + } + + return list, nil +} + +// CountByOrgUnit counts active custom roles for a specific organization unit +func (t *OrgUnitCustomRoleTable) CountByOrgUnit(ctx context.Context, tenant, orgUnitId string) (int32, error) { + filter := bson.M{ + "key.tenant": tenant, + "key.orgUnitId": orgUnitId, + "active": bson.M{"$ne": false}, // Include roles where active is true or nil + } + count, err := t.col.Count(ctx, filter) + return int32(count), err +} + +// GetByTenant retrieves all custom roles for a tenant across all org units +func (t *OrgUnitCustomRoleTable) GetByTenant(ctx context.Context, tenant string, offset, limit int32) ([]*OrgUnitCustomRole, error) { + filter := bson.M{ + "key.tenant": tenant, + "active": bson.M{"$ne": false}, // Include roles where active is true or nil + } + + list, err := t.FindMany(ctx, filter, offset, limit) + if err != nil { + return nil, err + } + + return list, nil +} + +// FindByNameAndOrgUnit finds a specific custom role by name within an org unit +func (t *OrgUnitCustomRoleTable) FindByNameAndOrgUnit(ctx context.Context, tenant, orgUnitId, roleName string) (*OrgUnitCustomRole, error) { + filter := bson.M{ + "key.tenant": tenant, // Filter by tenant + "key.orgUnitId": orgUnitId, // Filter by organization unit + "key.name": roleName, // Filter by role name + "active": bson.M{"$ne": false}, // Include only active roles (true or nil) + } + + // Use FindMany with limit 1 to get a single active role + results, err := t.FindMany(ctx, filter, 0, 1) + if err != nil { + return nil, err + } + + if len(results) == 0 { + return nil, errors.Wrapf(errors.NotFound, "custom role not found") + } + + return results[0], nil +} + +// SoftDelete marks a custom role as inactive instead of physically deleting it +func (t *OrgUnitCustomRoleTable) SoftDelete(ctx context.Context, key *OrgUnitCustomRoleKey, deletedBy string) error { + update := &OrgUnitCustomRole{ + Active: &[]bool{false}[0], // Mark as inactive + UpdatedBy: deletedBy, // Track who performed the deletion + Updated: time.Now().Unix(), // Update timestamp + } + + return t.Update(ctx, key, update) +} + +// HasBindings checks if a custom role has any users assigned to it +func (t *OrgUnitCustomRoleTable) HasBindings(ctx context.Context, tenant, orgUnitId, roleName string) (bool, error) { + // Get the org unit user table to check for role assignments + orgUnitUserTable, err := GetOrgUnitUserTable() + if err != nil { + return false, err + } + + // Check if any users are assigned this custom role + filter := bson.M{ + "key.tenant": tenant, + "key.orgUnitId": orgUnitId, + "role": roleName, + } + + count, err := orgUnitUserTable.col.Count(ctx, filter) + if err != nil { + return false, err + } + + return count > 0, nil +} + +// PermanentDelete permanently removes a custom role from the database +func (t *OrgUnitCustomRoleTable) PermanentDelete(ctx context.Context, key *OrgUnitCustomRoleKey) error { + return t.DeleteKey(ctx, key) +} + +// DeleteCustomRoleWithBindingCheck performs intelligent deletion based on binding status +func (t *OrgUnitCustomRoleTable) DeleteCustomRoleWithBindingCheck(ctx context.Context, key *OrgUnitCustomRoleKey, deletedBy string) error { + // Check if the role has any bindings + hasBindings, err := t.HasBindings(ctx, key.Tenant, key.OrgUnitId, key.Name) + if err != nil { + return err + } + + if hasBindings { + // Role has bindings - perform soft delete only + return t.SoftDelete(ctx, key, deletedBy) + } else { + // No bindings - permanently delete the role + return t.PermanentDelete(ctx, key) + } +} + +// FindAnyByNameAndOrgUnit finds a custom role by name (including soft-deleted ones) +// This is used to check for name conflicts including soft-deleted roles with bindings +func (t *OrgUnitCustomRoleTable) FindAnyByNameAndOrgUnit(ctx context.Context, tenant, orgUnitId, roleName string) (*OrgUnitCustomRole, error) { + filter := bson.M{ + "key.tenant": tenant, // Filter by tenant + "key.orgUnitId": orgUnitId, // Filter by organization unit + "key.name": roleName, // Filter by role name + // No active filter - find both active and inactive roles + } + + // Use FindMany with limit 1 to get any role (active or inactive) + results, err := t.FindMany(ctx, filter, 0, 1) + if err != nil { + return nil, err + } + + if len(results) == 0 { + return nil, errors.Wrapf(errors.NotFound, "custom role not found") + } + + return results[0], nil +} + +// CleanupOrphanedSoftDeletedRoles removes soft-deleted roles that no longer have bindings +func (t *OrgUnitCustomRoleTable) CleanupOrphanedSoftDeletedRoles(ctx context.Context, tenant, orgUnitId string) error { + // Find all soft-deleted roles + filter := bson.M{ + "key.tenant": tenant, + "key.orgUnitId": orgUnitId, + "active": false, // Only soft-deleted roles + } + + softDeletedRoles, err := t.FindMany(ctx, filter, 0, 0) // Get all + if err != nil { + return err + } + + // Check each soft-deleted role for bindings + for _, role := range softDeletedRoles { + hasBindings, err := t.HasBindings(ctx, tenant, orgUnitId, role.Key.Name) + if err != nil { + continue // Skip on error, don't fail the entire cleanup + } + + // If no bindings, permanently delete + if !hasBindings { + err = t.PermanentDelete(ctx, role.Key) + if err != nil { + // Log error but continue with other roles + continue + } + } + } + + return nil +} + +// StartEventLogger starts the event logger for the custom role table +func (t *OrgUnitCustomRoleTable) StartEventLogger() error { + logger := db.NewEventLogger[OrgUnitCustomRoleKey, OrgUnitCustomRole](t.col, nil) + return logger.Start(context.Background()) +} + +// GetOrgUnitCustomRoleTable returns the global custom role table instance +func GetOrgUnitCustomRoleTable() (*OrgUnitCustomRoleTable, error) { + if orgUnitCustomRoleTable != nil { + return orgUnitCustomRoleTable, nil + } + + return nil, errors.Wrapf(errors.NotFound, "org unit custom role table not found") +} + +// LocateOrgUnitCustomRoleTable initializes and returns the custom role table +func LocateOrgUnitCustomRoleTable(client db.StoreClient) (*OrgUnitCustomRoleTable, error) { + if orgUnitCustomRoleTable != nil { + return orgUnitCustomRoleTable, nil + } + + col := client.GetCollection(AuthDatabaseName, OrgUnitCustomRoleCollectionName) + tbl := &OrgUnitCustomRoleTable{ + col: col, + } + + err := tbl.Initialize(col) + if err != nil { + return nil, err + } + + orgUnitCustomRoleTable = tbl + + return orgUnitCustomRoleTable, nil +} From f450149a8620b53517297014408e1baa71f9ea3b Mon Sep 17 00:00:00 2001 From: suryanshu185 Date: Mon, 27 Oct 2025 11:10:12 +0530 Subject: [PATCH 3/3] feat: implement Log action for custom role permissions. Add Log action to the system to enable selective audit logging for sensitive operations. The Log action allows access while generating audit trail entries. Changes: - Add ROLE_PERMISSION_ACTION_LOG enum to protobuf definition (value 3) - Add RolePermissionActionLog constant to table package - Update permission evaluation to track log actions separately - Modify evaluateCustomRolePermissions to return (allowed, shouldLog) tuple - Add audit logging in performOrgUnitRoleCheck when Log action matches Audit log format includes: - User, Tenant, OrgUnit, Role - Resource, Verb, Instance - Access result (allowed/denied) Precedence rules: - Deny > Log/Allow (deny takes priority) - Log action allows access + marks for audit Logs are written to stdout/access.log with [LOG] prefix. Signed-off-by: suryanshu185 --- api/org-unit-role.pb.go | 499 ++++++++++++++++--------------- api/org-unit-role.proto | 92 +++--- api/swagger/apidocs.swagger.json | 83 ++--- pkg/gateway/server.go | 26 +- pkg/server/org-unit-role.go | 147 +++++---- pkg/table/org-unit-role.go | 3 +- 6 files changed, 421 insertions(+), 429 deletions(-) diff --git a/api/org-unit-role.pb.go b/api/org-unit-role.pb.go index 3b3fe54..d285f38 100644 --- a/api/org-unit-role.pb.go +++ b/api/org-unit-role.pb.go @@ -26,122 +26,124 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// Matching criteria types for resource-based permissions -type ResourceMatchCriteria int32 +type ResourceMatchCriteriaDefs_Criteria int32 const ( - // Unspecified/default (treated as wildcard) - ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_UNSPECIFIED ResourceMatchCriteria = 0 + // Unspecified/default (treated as deny for security) + ResourceMatchCriteriaDefs_Unspecified ResourceMatchCriteriaDefs_Criteria = 0 + // Matches all resources (wildcard) + ResourceMatchCriteriaDefs_Any ResourceMatchCriteriaDefs_Criteria = 1 // Exact resource name match - ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_EXACT ResourceMatchCriteria = 1 - // Resource name starts with the key - ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_PREFIX ResourceMatchCriteria = 2 - // Resource name ends with the key - ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_SUFFIX ResourceMatchCriteria = 3 - // Resource name matches the regex pattern in key - ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_REGEX ResourceMatchCriteria = 4 - // Key uses wildcard matching with * (e.g., "bucket-*", "*-prod", "*") - ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_WILDCARD ResourceMatchCriteria = 5 + ResourceMatchCriteriaDefs_Exact ResourceMatchCriteriaDefs_Criteria = 2 + // Resource name starts with the pattern + ResourceMatchCriteriaDefs_Prefix ResourceMatchCriteriaDefs_Criteria = 3 + // Resource name ends with the pattern + ResourceMatchCriteriaDefs_Suffix ResourceMatchCriteriaDefs_Criteria = 4 + // Resource name matches the regex pattern + ResourceMatchCriteriaDefs_Regex ResourceMatchCriteriaDefs_Criteria = 5 ) -// Enum value maps for ResourceMatchCriteria. +// Enum value maps for ResourceMatchCriteriaDefs_Criteria. var ( - ResourceMatchCriteria_name = map[int32]string{ - 0: "RESOURCE_MATCH_CRITERIA_UNSPECIFIED", - 1: "RESOURCE_MATCH_CRITERIA_EXACT", - 2: "RESOURCE_MATCH_CRITERIA_PREFIX", - 3: "RESOURCE_MATCH_CRITERIA_SUFFIX", - 4: "RESOURCE_MATCH_CRITERIA_REGEX", - 5: "RESOURCE_MATCH_CRITERIA_WILDCARD", + ResourceMatchCriteriaDefs_Criteria_name = map[int32]string{ + 0: "Unspecified", + 1: "Any", + 2: "Exact", + 3: "Prefix", + 4: "Suffix", + 5: "Regex", } - ResourceMatchCriteria_value = map[string]int32{ - "RESOURCE_MATCH_CRITERIA_UNSPECIFIED": 0, - "RESOURCE_MATCH_CRITERIA_EXACT": 1, - "RESOURCE_MATCH_CRITERIA_PREFIX": 2, - "RESOURCE_MATCH_CRITERIA_SUFFIX": 3, - "RESOURCE_MATCH_CRITERIA_REGEX": 4, - "RESOURCE_MATCH_CRITERIA_WILDCARD": 5, + ResourceMatchCriteriaDefs_Criteria_value = map[string]int32{ + "Unspecified": 0, + "Any": 1, + "Exact": 2, + "Prefix": 3, + "Suffix": 4, + "Regex": 5, } ) -func (x ResourceMatchCriteria) Enum() *ResourceMatchCriteria { - p := new(ResourceMatchCriteria) +func (x ResourceMatchCriteriaDefs_Criteria) Enum() *ResourceMatchCriteriaDefs_Criteria { + p := new(ResourceMatchCriteriaDefs_Criteria) *p = x return p } -func (x ResourceMatchCriteria) String() string { +func (x ResourceMatchCriteriaDefs_Criteria) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (ResourceMatchCriteria) Descriptor() protoreflect.EnumDescriptor { +func (ResourceMatchCriteriaDefs_Criteria) Descriptor() protoreflect.EnumDescriptor { return file_org_unit_role_proto_enumTypes[0].Descriptor() } -func (ResourceMatchCriteria) Type() protoreflect.EnumType { +func (ResourceMatchCriteriaDefs_Criteria) Type() protoreflect.EnumType { return &file_org_unit_role_proto_enumTypes[0] } -func (x ResourceMatchCriteria) Number() protoreflect.EnumNumber { +func (x ResourceMatchCriteriaDefs_Criteria) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } -// Deprecated: Use ResourceMatchCriteria.Descriptor instead. -func (ResourceMatchCriteria) EnumDescriptor() ([]byte, []int) { - return file_org_unit_role_proto_rawDescGZIP(), []int{0} +// Deprecated: Use ResourceMatchCriteriaDefs_Criteria.Descriptor instead. +func (ResourceMatchCriteriaDefs_Criteria) EnumDescriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{3, 0} } -// Action types for role permissions -type RolePermissionAction int32 +type RolePermissionActionDefs_Action int32 const ( - // Unspecified action (invalid) - RolePermissionAction_ROLE_PERMISSION_ACTION_UNSPECIFIED RolePermissionAction = 0 + // Unspecified action (invalid, will be denied) + RolePermissionActionDefs_Unspecified RolePermissionActionDefs_Action = 0 // Allow the specified verbs on the resource - RolePermissionAction_ROLE_PERMISSION_ACTION_ALLOW RolePermissionAction = 1 + RolePermissionActionDefs_Allow RolePermissionActionDefs_Action = 1 // Deny the specified verbs on the resource (takes precedence over Allow) - RolePermissionAction_ROLE_PERMISSION_ACTION_DENY RolePermissionAction = 2 + RolePermissionActionDefs_Deny RolePermissionActionDefs_Action = 2 + // Log the specified verbs on the resource (allows access but logs for audit) + RolePermissionActionDefs_Log RolePermissionActionDefs_Action = 3 ) -// Enum value maps for RolePermissionAction. +// Enum value maps for RolePermissionActionDefs_Action. var ( - RolePermissionAction_name = map[int32]string{ - 0: "ROLE_PERMISSION_ACTION_UNSPECIFIED", - 1: "ROLE_PERMISSION_ACTION_ALLOW", - 2: "ROLE_PERMISSION_ACTION_DENY", + RolePermissionActionDefs_Action_name = map[int32]string{ + 0: "Unspecified", + 1: "Allow", + 2: "Deny", + 3: "Log", } - RolePermissionAction_value = map[string]int32{ - "ROLE_PERMISSION_ACTION_UNSPECIFIED": 0, - "ROLE_PERMISSION_ACTION_ALLOW": 1, - "ROLE_PERMISSION_ACTION_DENY": 2, + RolePermissionActionDefs_Action_value = map[string]int32{ + "Unspecified": 0, + "Allow": 1, + "Deny": 2, + "Log": 3, } ) -func (x RolePermissionAction) Enum() *RolePermissionAction { - p := new(RolePermissionAction) +func (x RolePermissionActionDefs_Action) Enum() *RolePermissionActionDefs_Action { + p := new(RolePermissionActionDefs_Action) *p = x return p } -func (x RolePermissionAction) String() string { +func (x RolePermissionActionDefs_Action) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (RolePermissionAction) Descriptor() protoreflect.EnumDescriptor { +func (RolePermissionActionDefs_Action) Descriptor() protoreflect.EnumDescriptor { return file_org_unit_role_proto_enumTypes[1].Descriptor() } -func (RolePermissionAction) Type() protoreflect.EnumType { +func (RolePermissionActionDefs_Action) Type() protoreflect.EnumType { return &file_org_unit_role_proto_enumTypes[1] } -func (x RolePermissionAction) Number() protoreflect.EnumNumber { +func (x RolePermissionActionDefs_Action) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } -// Deprecated: Use RolePermissionAction.Descriptor instead. -func (RolePermissionAction) EnumDescriptor() ([]byte, []int) { - return file_org_unit_role_proto_rawDescGZIP(), []int{1} +// Deprecated: Use RolePermissionActionDefs_Action.Descriptor instead. +func (RolePermissionActionDefs_Action) EnumDescriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{4, 0} } // org unit roles list request @@ -198,12 +200,10 @@ type OrgUnitRolesListEntry struct { Desc string `protobuf:"bytes,2,opt,name=desc,proto3" json:"desc,omitempty"` // role type: "built-in" for system roles (admin, auditor) or "custom" for user-defined roles Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` - // display name for custom roles (only applicable for custom roles) - DisplayName string `protobuf:"bytes,4,opt,name=displayName,proto3" json:"displayName,omitempty"` // creation timestamp for custom roles (only applicable for custom roles) - Created int64 `protobuf:"varint,5,opt,name=created,proto3" json:"created,omitempty"` + Created int64 `protobuf:"varint,4,opt,name=created,proto3" json:"created,omitempty"` // user who created the role for custom roles (only applicable for custom roles) - CreatedBy string `protobuf:"bytes,6,opt,name=createdBy,proto3" json:"createdBy,omitempty"` + CreatedBy string `protobuf:"bytes,5,opt,name=createdBy,proto3" json:"createdBy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -259,13 +259,6 @@ func (x *OrgUnitRolesListEntry) GetType() string { return "" } -func (x *OrgUnitRolesListEntry) GetDisplayName() string { - if x != nil { - return x.DisplayName - } - return "" -} - func (x *OrgUnitRolesListEntry) GetCreated() int64 { if x != nil { return x.Created @@ -326,11 +319,85 @@ func (x *OrgUnitRolesListResp) GetItems() []*OrgUnitRolesListEntry { return nil } +// Matching criteria types for resource-based permissions +type ResourceMatchCriteriaDefs struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ResourceMatchCriteriaDefs) Reset() { + *x = ResourceMatchCriteriaDefs{} + mi := &file_org_unit_role_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ResourceMatchCriteriaDefs) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResourceMatchCriteriaDefs) ProtoMessage() {} + +func (x *ResourceMatchCriteriaDefs) ProtoReflect() protoreflect.Message { + mi := &file_org_unit_role_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 ResourceMatchCriteriaDefs.ProtoReflect.Descriptor instead. +func (*ResourceMatchCriteriaDefs) Descriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{3} +} + +// Action types for role permissions +type RolePermissionActionDefs struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RolePermissionActionDefs) Reset() { + *x = RolePermissionActionDefs{} + mi := &file_org_unit_role_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RolePermissionActionDefs) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RolePermissionActionDefs) ProtoMessage() {} + +func (x *RolePermissionActionDefs) ProtoReflect() protoreflect.Message { + mi := &file_org_unit_role_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 RolePermissionActionDefs.ProtoReflect.Descriptor instead. +func (*RolePermissionActionDefs) Descriptor() ([]byte, []int) { + return file_org_unit_role_proto_rawDescGZIP(), []int{4} +} + // Resource matching criteria for fine-grained permission control type ResourceMatch struct { state protoimpl.MessageState `protogen:"open.v1"` // Matching criteria type - Criteria ResourceMatchCriteria `protobuf:"varint,1,opt,name=criteria,proto3,enum=api.ResourceMatchCriteria" json:"criteria,omitempty"` + Criteria ResourceMatchCriteriaDefs_Criteria `protobuf:"varint,1,opt,name=criteria,proto3,enum=api.ResourceMatchCriteriaDefs_Criteria" json:"criteria,omitempty"` // The matching key/pattern based on criteria // For wildcard: supports * (e.g., "bucket-*", "*-prod", "*") // For regex: valid regex pattern @@ -342,7 +409,7 @@ type ResourceMatch struct { func (x *ResourceMatch) Reset() { *x = ResourceMatch{} - mi := &file_org_unit_role_proto_msgTypes[3] + mi := &file_org_unit_role_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -354,7 +421,7 @@ func (x *ResourceMatch) String() string { func (*ResourceMatch) ProtoMessage() {} func (x *ResourceMatch) ProtoReflect() protoreflect.Message { - mi := &file_org_unit_role_proto_msgTypes[3] + mi := &file_org_unit_role_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -367,14 +434,14 @@ func (x *ResourceMatch) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceMatch.ProtoReflect.Descriptor instead. func (*ResourceMatch) Descriptor() ([]byte, []int) { - return file_org_unit_role_proto_rawDescGZIP(), []int{3} + return file_org_unit_role_proto_rawDescGZIP(), []int{5} } -func (x *ResourceMatch) GetCriteria() ResourceMatchCriteria { +func (x *ResourceMatch) GetCriteria() ResourceMatchCriteriaDefs_Criteria { if x != nil { return x.Criteria } - return ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_UNSPECIFIED + return ResourceMatchCriteriaDefs_Unspecified } func (x *ResourceMatch) GetKey() string { @@ -393,15 +460,15 @@ type RolePermission struct { Match *ResourceMatch `protobuf:"bytes,2,opt,name=match,proto3" json:"match,omitempty"` // List of allowed verbs/actions for this resource (supports "*" for all verbs) Verbs []string `protobuf:"bytes,3,rep,name=verbs,proto3" json:"verbs,omitempty"` - // Action type: Allow or Deny (Deny takes precedence over Allow) - Action RolePermissionAction `protobuf:"varint,4,opt,name=action,proto3,enum=api.RolePermissionAction" json:"action,omitempty"` + // Action type: Allow, Deny, or Log + Action RolePermissionActionDefs_Action `protobuf:"varint,4,opt,name=action,proto3,enum=api.RolePermissionActionDefs_Action" json:"action,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RolePermission) Reset() { *x = RolePermission{} - mi := &file_org_unit_role_proto_msgTypes[4] + mi := &file_org_unit_role_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -413,7 +480,7 @@ func (x *RolePermission) String() string { func (*RolePermission) ProtoMessage() {} func (x *RolePermission) ProtoReflect() protoreflect.Message { - mi := &file_org_unit_role_proto_msgTypes[4] + mi := &file_org_unit_role_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -426,7 +493,7 @@ func (x *RolePermission) ProtoReflect() protoreflect.Message { // Deprecated: Use RolePermission.ProtoReflect.Descriptor instead. func (*RolePermission) Descriptor() ([]byte, []int) { - return file_org_unit_role_proto_rawDescGZIP(), []int{4} + return file_org_unit_role_proto_rawDescGZIP(), []int{6} } func (x *RolePermission) GetResource() string { @@ -450,11 +517,11 @@ func (x *RolePermission) GetVerbs() []string { return nil } -func (x *RolePermission) GetAction() RolePermissionAction { +func (x *RolePermission) GetAction() RolePermissionActionDefs_Action { if x != nil { return x.Action } - return RolePermissionAction_ROLE_PERMISSION_ACTION_UNSPECIFIED + return RolePermissionActionDefs_Unspecified } // Create custom role request @@ -464,19 +531,17 @@ type CreateCustomRoleReq struct { Ou string `protobuf:"bytes,1,opt,name=ou,proto3" json:"ou,omitempty"` // Name of the custom role (must be unique within the org unit) Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - // Display name for the custom role - DisplayName string `protobuf:"bytes,3,opt,name=displayName,proto3" json:"displayName,omitempty"` // Description explaining the purpose of this custom role - Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // List of permissions granted by this custom role - Permissions []*RolePermission `protobuf:"bytes,5,rep,name=permissions,proto3" json:"permissions,omitempty"` + Permissions []*RolePermission `protobuf:"bytes,4,rep,name=permissions,proto3" json:"permissions,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CreateCustomRoleReq) Reset() { *x = CreateCustomRoleReq{} - mi := &file_org_unit_role_proto_msgTypes[5] + mi := &file_org_unit_role_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -488,7 +553,7 @@ func (x *CreateCustomRoleReq) String() string { func (*CreateCustomRoleReq) ProtoMessage() {} func (x *CreateCustomRoleReq) ProtoReflect() protoreflect.Message { - mi := &file_org_unit_role_proto_msgTypes[5] + mi := &file_org_unit_role_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -501,7 +566,7 @@ func (x *CreateCustomRoleReq) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateCustomRoleReq.ProtoReflect.Descriptor instead. func (*CreateCustomRoleReq) Descriptor() ([]byte, []int) { - return file_org_unit_role_proto_rawDescGZIP(), []int{5} + return file_org_unit_role_proto_rawDescGZIP(), []int{7} } func (x *CreateCustomRoleReq) GetOu() string { @@ -518,13 +583,6 @@ func (x *CreateCustomRoleReq) GetName() string { return "" } -func (x *CreateCustomRoleReq) GetDisplayName() string { - if x != nil { - return x.DisplayName - } - return "" -} - func (x *CreateCustomRoleReq) GetDescription() string { if x != nil { return x.Description @@ -541,16 +599,14 @@ func (x *CreateCustomRoleReq) GetPermissions() []*RolePermission { // Create custom role response type CreateCustomRoleResp struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Confirmation message - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CreateCustomRoleResp) Reset() { *x = CreateCustomRoleResp{} - mi := &file_org_unit_role_proto_msgTypes[6] + mi := &file_org_unit_role_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -562,7 +618,7 @@ func (x *CreateCustomRoleResp) String() string { func (*CreateCustomRoleResp) ProtoMessage() {} func (x *CreateCustomRoleResp) ProtoReflect() protoreflect.Message { - mi := &file_org_unit_role_proto_msgTypes[6] + mi := &file_org_unit_role_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -575,14 +631,7 @@ func (x *CreateCustomRoleResp) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateCustomRoleResp.ProtoReflect.Descriptor instead. func (*CreateCustomRoleResp) Descriptor() ([]byte, []int) { - return file_org_unit_role_proto_rawDescGZIP(), []int{6} -} - -func (x *CreateCustomRoleResp) GetMessage() string { - if x != nil { - return x.Message - } - return "" + return file_org_unit_role_proto_rawDescGZIP(), []int{8} } // Update custom role request @@ -592,19 +641,17 @@ type UpdateCustomRoleReq struct { Ou string `protobuf:"bytes,1,opt,name=ou,proto3" json:"ou,omitempty"` // Name of the custom role to update Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - // Updated display name for the custom role - DisplayName string `protobuf:"bytes,3,opt,name=displayName,proto3" json:"displayName,omitempty"` // Updated description explaining the purpose of this custom role - Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // Updated list of permissions granted by this custom role - Permissions []*RolePermission `protobuf:"bytes,5,rep,name=permissions,proto3" json:"permissions,omitempty"` + Permissions []*RolePermission `protobuf:"bytes,4,rep,name=permissions,proto3" json:"permissions,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UpdateCustomRoleReq) Reset() { *x = UpdateCustomRoleReq{} - mi := &file_org_unit_role_proto_msgTypes[7] + mi := &file_org_unit_role_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -616,7 +663,7 @@ func (x *UpdateCustomRoleReq) String() string { func (*UpdateCustomRoleReq) ProtoMessage() {} func (x *UpdateCustomRoleReq) ProtoReflect() protoreflect.Message { - mi := &file_org_unit_role_proto_msgTypes[7] + mi := &file_org_unit_role_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -629,7 +676,7 @@ func (x *UpdateCustomRoleReq) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateCustomRoleReq.ProtoReflect.Descriptor instead. func (*UpdateCustomRoleReq) Descriptor() ([]byte, []int) { - return file_org_unit_role_proto_rawDescGZIP(), []int{7} + return file_org_unit_role_proto_rawDescGZIP(), []int{9} } func (x *UpdateCustomRoleReq) GetOu() string { @@ -646,13 +693,6 @@ func (x *UpdateCustomRoleReq) GetName() string { return "" } -func (x *UpdateCustomRoleReq) GetDisplayName() string { - if x != nil { - return x.DisplayName - } - return "" -} - func (x *UpdateCustomRoleReq) GetDescription() string { if x != nil { return x.Description @@ -669,16 +709,14 @@ func (x *UpdateCustomRoleReq) GetPermissions() []*RolePermission { // Update custom role response type UpdateCustomRoleResp struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Confirmation message - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UpdateCustomRoleResp) Reset() { *x = UpdateCustomRoleResp{} - mi := &file_org_unit_role_proto_msgTypes[8] + mi := &file_org_unit_role_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -690,7 +728,7 @@ func (x *UpdateCustomRoleResp) String() string { func (*UpdateCustomRoleResp) ProtoMessage() {} func (x *UpdateCustomRoleResp) ProtoReflect() protoreflect.Message { - mi := &file_org_unit_role_proto_msgTypes[8] + mi := &file_org_unit_role_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -703,14 +741,7 @@ func (x *UpdateCustomRoleResp) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateCustomRoleResp.ProtoReflect.Descriptor instead. func (*UpdateCustomRoleResp) Descriptor() ([]byte, []int) { - return file_org_unit_role_proto_rawDescGZIP(), []int{8} -} - -func (x *UpdateCustomRoleResp) GetMessage() string { - if x != nil { - return x.Message - } - return "" + return file_org_unit_role_proto_rawDescGZIP(), []int{10} } // Get custom role request @@ -726,7 +757,7 @@ type GetCustomRoleReq struct { func (x *GetCustomRoleReq) Reset() { *x = GetCustomRoleReq{} - mi := &file_org_unit_role_proto_msgTypes[9] + mi := &file_org_unit_role_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -738,7 +769,7 @@ func (x *GetCustomRoleReq) String() string { func (*GetCustomRoleReq) ProtoMessage() {} func (x *GetCustomRoleReq) ProtoReflect() protoreflect.Message { - mi := &file_org_unit_role_proto_msgTypes[9] + mi := &file_org_unit_role_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -751,7 +782,7 @@ func (x *GetCustomRoleReq) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCustomRoleReq.ProtoReflect.Descriptor instead. func (*GetCustomRoleReq) Descriptor() ([]byte, []int) { - return file_org_unit_role_proto_rawDescGZIP(), []int{9} + return file_org_unit_role_proto_rawDescGZIP(), []int{11} } func (x *GetCustomRoleReq) GetOu() string { @@ -773,27 +804,25 @@ type GetCustomRoleResp struct { state protoimpl.MessageState `protogen:"open.v1"` // Name of the custom role Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Display name for the custom role - DisplayName string `protobuf:"bytes,2,opt,name=displayName,proto3" json:"displayName,omitempty"` // Description explaining the purpose of this custom role - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` // List of permissions granted by this custom role - Permissions []*RolePermission `protobuf:"bytes,4,rep,name=permissions,proto3" json:"permissions,omitempty"` + Permissions []*RolePermission `protobuf:"bytes,3,rep,name=permissions,proto3" json:"permissions,omitempty"` // Timestamp when the role was created - Created int64 `protobuf:"varint,5,opt,name=created,proto3" json:"created,omitempty"` + Created int64 `protobuf:"varint,4,opt,name=created,proto3" json:"created,omitempty"` // User who created this custom role - CreatedBy string `protobuf:"bytes,6,opt,name=createdBy,proto3" json:"createdBy,omitempty"` + CreatedBy string `protobuf:"bytes,5,opt,name=createdBy,proto3" json:"createdBy,omitempty"` // Timestamp when the role was last updated - Updated int64 `protobuf:"varint,7,opt,name=updated,proto3" json:"updated,omitempty"` + Updated int64 `protobuf:"varint,6,opt,name=updated,proto3" json:"updated,omitempty"` // User who last updated this custom role - UpdatedBy string `protobuf:"bytes,8,opt,name=updatedBy,proto3" json:"updatedBy,omitempty"` + UpdatedBy string `protobuf:"bytes,7,opt,name=updatedBy,proto3" json:"updatedBy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetCustomRoleResp) Reset() { *x = GetCustomRoleResp{} - mi := &file_org_unit_role_proto_msgTypes[10] + mi := &file_org_unit_role_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -805,7 +834,7 @@ func (x *GetCustomRoleResp) String() string { func (*GetCustomRoleResp) ProtoMessage() {} func (x *GetCustomRoleResp) ProtoReflect() protoreflect.Message { - mi := &file_org_unit_role_proto_msgTypes[10] + mi := &file_org_unit_role_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -818,7 +847,7 @@ func (x *GetCustomRoleResp) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCustomRoleResp.ProtoReflect.Descriptor instead. func (*GetCustomRoleResp) Descriptor() ([]byte, []int) { - return file_org_unit_role_proto_rawDescGZIP(), []int{10} + return file_org_unit_role_proto_rawDescGZIP(), []int{12} } func (x *GetCustomRoleResp) GetName() string { @@ -828,13 +857,6 @@ func (x *GetCustomRoleResp) GetName() string { return "" } -func (x *GetCustomRoleResp) GetDisplayName() string { - if x != nil { - return x.DisplayName - } - return "" -} - func (x *GetCustomRoleResp) GetDescription() string { if x != nil { return x.Description @@ -890,7 +912,7 @@ type DeleteCustomRoleReq struct { func (x *DeleteCustomRoleReq) Reset() { *x = DeleteCustomRoleReq{} - mi := &file_org_unit_role_proto_msgTypes[11] + mi := &file_org_unit_role_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -902,7 +924,7 @@ func (x *DeleteCustomRoleReq) String() string { func (*DeleteCustomRoleReq) ProtoMessage() {} func (x *DeleteCustomRoleReq) ProtoReflect() protoreflect.Message { - mi := &file_org_unit_role_proto_msgTypes[11] + mi := &file_org_unit_role_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -915,7 +937,7 @@ func (x *DeleteCustomRoleReq) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteCustomRoleReq.ProtoReflect.Descriptor instead. func (*DeleteCustomRoleReq) Descriptor() ([]byte, []int) { - return file_org_unit_role_proto_rawDescGZIP(), []int{11} + return file_org_unit_role_proto_rawDescGZIP(), []int{13} } func (x *DeleteCustomRoleReq) GetOu() string { @@ -943,7 +965,7 @@ type DeleteCustomRoleResp struct { func (x *DeleteCustomRoleResp) Reset() { *x = DeleteCustomRoleResp{} - mi := &file_org_unit_role_proto_msgTypes[12] + mi := &file_org_unit_role_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -955,7 +977,7 @@ func (x *DeleteCustomRoleResp) String() string { func (*DeleteCustomRoleResp) ProtoMessage() {} func (x *DeleteCustomRoleResp) ProtoReflect() protoreflect.Message { - mi := &file_org_unit_role_proto_msgTypes[12] + mi := &file_org_unit_role_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -968,7 +990,7 @@ func (x *DeleteCustomRoleResp) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteCustomRoleResp.ProtoReflect.Descriptor instead. func (*DeleteCustomRoleResp) Descriptor() ([]byte, []int) { - return file_org_unit_role_proto_rawDescGZIP(), []int{12} + return file_org_unit_role_proto_rawDescGZIP(), []int{14} } func (x *DeleteCustomRoleResp) GetMessage() string { @@ -984,68 +1006,67 @@ const file_org_unit_role_proto_rawDesc = "" + "\n" + "\x13org-unit-role.proto\x12\x03api\x1a\x1cgoogle/api/annotations.proto\x1a\x17coreapis/api/role.proto\"%\n" + "\x13OrgUnitRolesListReq\x12\x0e\n" + - "\x02ou\x18\x01 \x01(\tR\x02ou\"\xad\x01\n" + + "\x02ou\x18\x01 \x01(\tR\x02ou\"\x8b\x01\n" + "\x15OrgUnitRolesListEntry\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" + "\x04desc\x18\x02 \x01(\tR\x04desc\x12\x12\n" + - "\x04type\x18\x03 \x01(\tR\x04type\x12 \n" + - "\vdisplayName\x18\x04 \x01(\tR\vdisplayName\x12\x18\n" + - "\acreated\x18\x05 \x01(\x03R\acreated\x12\x1c\n" + - "\tcreatedBy\x18\x06 \x01(\tR\tcreatedBy\"H\n" + + "\x04type\x18\x03 \x01(\tR\x04type\x12\x18\n" + + "\acreated\x18\x04 \x01(\x03R\acreated\x12\x1c\n" + + "\tcreatedBy\x18\x05 \x01(\tR\tcreatedBy\"H\n" + "\x14OrgUnitRolesListResp\x120\n" + - "\x05items\x18\x02 \x03(\v2\x1a.api.OrgUnitRolesListEntryR\x05items\"Y\n" + - "\rResourceMatch\x126\n" + - "\bcriteria\x18\x01 \x01(\x0e2\x1a.api.ResourceMatchCriteriaR\bcriteria\x12\x10\n" + - "\x03key\x18\x02 \x01(\tR\x03key\"\x9f\x01\n" + + "\x05items\x18\x02 \x03(\v2\x1a.api.OrgUnitRolesListEntryR\x05items\"o\n" + + "\x19ResourceMatchCriteriaDefs\"R\n" + + "\bCriteria\x12\x0f\n" + + "\vUnspecified\x10\x00\x12\a\n" + + "\x03Any\x10\x01\x12\t\n" + + "\x05Exact\x10\x02\x12\n" + + "\n" + + "\x06Prefix\x10\x03\x12\n" + + "\n" + + "\x06Suffix\x10\x04\x12\t\n" + + "\x05Regex\x10\x05\"S\n" + + "\x18RolePermissionActionDefs\"7\n" + + "\x06Action\x12\x0f\n" + + "\vUnspecified\x10\x00\x12\t\n" + + "\x05Allow\x10\x01\x12\b\n" + + "\x04Deny\x10\x02\x12\a\n" + + "\x03Log\x10\x03\"f\n" + + "\rResourceMatch\x12C\n" + + "\bcriteria\x18\x01 \x01(\x0e2'.api.ResourceMatchCriteriaDefs.CriteriaR\bcriteria\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\"\xaa\x01\n" + "\x0eRolePermission\x12\x1a\n" + "\bresource\x18\x01 \x01(\tR\bresource\x12(\n" + "\x05match\x18\x02 \x01(\v2\x12.api.ResourceMatchR\x05match\x12\x14\n" + - "\x05verbs\x18\x03 \x03(\tR\x05verbs\x121\n" + - "\x06action\x18\x04 \x01(\x0e2\x19.api.RolePermissionActionR\x06action\"\xb4\x01\n" + + "\x05verbs\x18\x03 \x03(\tR\x05verbs\x12<\n" + + "\x06action\x18\x04 \x01(\x0e2$.api.RolePermissionActionDefs.ActionR\x06action\"\x92\x01\n" + "\x13CreateCustomRoleReq\x12\x0e\n" + "\x02ou\x18\x01 \x01(\tR\x02ou\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x12 \n" + - "\vdisplayName\x18\x03 \x01(\tR\vdisplayName\x12 \n" + - "\vdescription\x18\x04 \x01(\tR\vdescription\x125\n" + - "\vpermissions\x18\x05 \x03(\v2\x13.api.RolePermissionR\vpermissions\"0\n" + - "\x14CreateCustomRoleResp\x12\x18\n" + - "\amessage\x18\x01 \x01(\tR\amessage\"\xb4\x01\n" + + "\vdescription\x18\x03 \x01(\tR\vdescription\x125\n" + + "\vpermissions\x18\x04 \x03(\v2\x13.api.RolePermissionR\vpermissions\"\x16\n" + + "\x14CreateCustomRoleResp\"\x92\x01\n" + "\x13UpdateCustomRoleReq\x12\x0e\n" + "\x02ou\x18\x01 \x01(\tR\x02ou\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x12 \n" + - "\vdisplayName\x18\x03 \x01(\tR\vdisplayName\x12 \n" + - "\vdescription\x18\x04 \x01(\tR\vdescription\x125\n" + - "\vpermissions\x18\x05 \x03(\v2\x13.api.RolePermissionR\vpermissions\"0\n" + - "\x14UpdateCustomRoleResp\x12\x18\n" + - "\amessage\x18\x01 \x01(\tR\amessage\"6\n" + + "\vdescription\x18\x03 \x01(\tR\vdescription\x125\n" + + "\vpermissions\x18\x04 \x03(\v2\x13.api.RolePermissionR\vpermissions\"\x16\n" + + "\x14UpdateCustomRoleResp\"6\n" + "\x10GetCustomRoleReq\x12\x0e\n" + "\x02ou\x18\x01 \x01(\tR\x02ou\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\"\x92\x02\n" + + "\x04name\x18\x02 \x01(\tR\x04name\"\xf0\x01\n" + "\x11GetCustomRoleResp\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12 \n" + - "\vdisplayName\x18\x02 \x01(\tR\vdisplayName\x12 \n" + - "\vdescription\x18\x03 \x01(\tR\vdescription\x125\n" + - "\vpermissions\x18\x04 \x03(\v2\x13.api.RolePermissionR\vpermissions\x12\x18\n" + - "\acreated\x18\x05 \x01(\x03R\acreated\x12\x1c\n" + - "\tcreatedBy\x18\x06 \x01(\tR\tcreatedBy\x12\x18\n" + - "\aupdated\x18\a \x01(\x03R\aupdated\x12\x1c\n" + - "\tupdatedBy\x18\b \x01(\tR\tupdatedBy\"9\n" + + "\vdescription\x18\x02 \x01(\tR\vdescription\x125\n" + + "\vpermissions\x18\x03 \x03(\v2\x13.api.RolePermissionR\vpermissions\x12\x18\n" + + "\acreated\x18\x04 \x01(\x03R\acreated\x12\x1c\n" + + "\tcreatedBy\x18\x05 \x01(\tR\tcreatedBy\x12\x18\n" + + "\aupdated\x18\x06 \x01(\x03R\aupdated\x12\x1c\n" + + "\tupdatedBy\x18\a \x01(\tR\tupdatedBy\"9\n" + "\x13DeleteCustomRoleReq\x12\x0e\n" + "\x02ou\x18\x01 \x01(\tR\x02ou\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\"0\n" + "\x14DeleteCustomRoleResp\x12\x18\n" + - "\amessage\x18\x01 \x01(\tR\amessage*\xf4\x01\n" + - "\x15ResourceMatchCriteria\x12'\n" + - "#RESOURCE_MATCH_CRITERIA_UNSPECIFIED\x10\x00\x12!\n" + - "\x1dRESOURCE_MATCH_CRITERIA_EXACT\x10\x01\x12\"\n" + - "\x1eRESOURCE_MATCH_CRITERIA_PREFIX\x10\x02\x12\"\n" + - "\x1eRESOURCE_MATCH_CRITERIA_SUFFIX\x10\x03\x12!\n" + - "\x1dRESOURCE_MATCH_CRITERIA_REGEX\x10\x04\x12$\n" + - " RESOURCE_MATCH_CRITERIA_WILDCARD\x10\x05*\x81\x01\n" + - "\x14RolePermissionAction\x12&\n" + - "\"ROLE_PERMISSION_ACTION_UNSPECIFIED\x10\x00\x12 \n" + - "\x1cROLE_PERMISSION_ACTION_ALLOW\x10\x01\x12\x1f\n" + - "\x1bROLE_PERMISSION_ACTION_DENY\x10\x022\xd7\x05\n" + + "\amessage\x18\x01 \x01(\tR\amessage2\xd7\x05\n" + "\vOrgUnitRole\x12\x88\x01\n" + "\x10ListOrgUnitRoles\x12\x18.api.OrgUnitRolesListReq\x1a\x19.api.OrgUnitRolesListResp\"?\x8a\xb5\x18\x19\n" + "\rorg-unit-role\x12\x02ou\x1a\x04list\x82\xd3\xe4\x93\x02\x1c\x12\x1a/api/auth/v1/ou/{ou}/roles\x12\x8c\x01\n" + @@ -1071,42 +1092,44 @@ func file_org_unit_role_proto_rawDescGZIP() []byte { } var file_org_unit_role_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_org_unit_role_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_org_unit_role_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_org_unit_role_proto_goTypes = []any{ - (ResourceMatchCriteria)(0), // 0: api.ResourceMatchCriteria - (RolePermissionAction)(0), // 1: api.RolePermissionAction - (*OrgUnitRolesListReq)(nil), // 2: api.OrgUnitRolesListReq - (*OrgUnitRolesListEntry)(nil), // 3: api.OrgUnitRolesListEntry - (*OrgUnitRolesListResp)(nil), // 4: api.OrgUnitRolesListResp - (*ResourceMatch)(nil), // 5: api.ResourceMatch - (*RolePermission)(nil), // 6: api.RolePermission - (*CreateCustomRoleReq)(nil), // 7: api.CreateCustomRoleReq - (*CreateCustomRoleResp)(nil), // 8: api.CreateCustomRoleResp - (*UpdateCustomRoleReq)(nil), // 9: api.UpdateCustomRoleReq - (*UpdateCustomRoleResp)(nil), // 10: api.UpdateCustomRoleResp - (*GetCustomRoleReq)(nil), // 11: api.GetCustomRoleReq - (*GetCustomRoleResp)(nil), // 12: api.GetCustomRoleResp - (*DeleteCustomRoleReq)(nil), // 13: api.DeleteCustomRoleReq - (*DeleteCustomRoleResp)(nil), // 14: api.DeleteCustomRoleResp + (ResourceMatchCriteriaDefs_Criteria)(0), // 0: api.ResourceMatchCriteriaDefs.Criteria + (RolePermissionActionDefs_Action)(0), // 1: api.RolePermissionActionDefs.Action + (*OrgUnitRolesListReq)(nil), // 2: api.OrgUnitRolesListReq + (*OrgUnitRolesListEntry)(nil), // 3: api.OrgUnitRolesListEntry + (*OrgUnitRolesListResp)(nil), // 4: api.OrgUnitRolesListResp + (*ResourceMatchCriteriaDefs)(nil), // 5: api.ResourceMatchCriteriaDefs + (*RolePermissionActionDefs)(nil), // 6: api.RolePermissionActionDefs + (*ResourceMatch)(nil), // 7: api.ResourceMatch + (*RolePermission)(nil), // 8: api.RolePermission + (*CreateCustomRoleReq)(nil), // 9: api.CreateCustomRoleReq + (*CreateCustomRoleResp)(nil), // 10: api.CreateCustomRoleResp + (*UpdateCustomRoleReq)(nil), // 11: api.UpdateCustomRoleReq + (*UpdateCustomRoleResp)(nil), // 12: api.UpdateCustomRoleResp + (*GetCustomRoleReq)(nil), // 13: api.GetCustomRoleReq + (*GetCustomRoleResp)(nil), // 14: api.GetCustomRoleResp + (*DeleteCustomRoleReq)(nil), // 15: api.DeleteCustomRoleReq + (*DeleteCustomRoleResp)(nil), // 16: api.DeleteCustomRoleResp } var file_org_unit_role_proto_depIdxs = []int32{ 3, // 0: api.OrgUnitRolesListResp.items:type_name -> api.OrgUnitRolesListEntry - 0, // 1: api.ResourceMatch.criteria:type_name -> api.ResourceMatchCriteria - 5, // 2: api.RolePermission.match:type_name -> api.ResourceMatch - 1, // 3: api.RolePermission.action:type_name -> api.RolePermissionAction - 6, // 4: api.CreateCustomRoleReq.permissions:type_name -> api.RolePermission - 6, // 5: api.UpdateCustomRoleReq.permissions:type_name -> api.RolePermission - 6, // 6: api.GetCustomRoleResp.permissions:type_name -> api.RolePermission + 0, // 1: api.ResourceMatch.criteria:type_name -> api.ResourceMatchCriteriaDefs.Criteria + 7, // 2: api.RolePermission.match:type_name -> api.ResourceMatch + 1, // 3: api.RolePermission.action:type_name -> api.RolePermissionActionDefs.Action + 8, // 4: api.CreateCustomRoleReq.permissions:type_name -> api.RolePermission + 8, // 5: api.UpdateCustomRoleReq.permissions:type_name -> api.RolePermission + 8, // 6: api.GetCustomRoleResp.permissions:type_name -> api.RolePermission 2, // 7: api.OrgUnitRole.ListOrgUnitRoles:input_type -> api.OrgUnitRolesListReq - 7, // 8: api.OrgUnitRole.CreateCustomRole:input_type -> api.CreateCustomRoleReq - 9, // 9: api.OrgUnitRole.UpdateCustomRole:input_type -> api.UpdateCustomRoleReq - 11, // 10: api.OrgUnitRole.GetCustomRole:input_type -> api.GetCustomRoleReq - 13, // 11: api.OrgUnitRole.DeleteCustomRole:input_type -> api.DeleteCustomRoleReq + 9, // 8: api.OrgUnitRole.CreateCustomRole:input_type -> api.CreateCustomRoleReq + 11, // 9: api.OrgUnitRole.UpdateCustomRole:input_type -> api.UpdateCustomRoleReq + 13, // 10: api.OrgUnitRole.GetCustomRole:input_type -> api.GetCustomRoleReq + 15, // 11: api.OrgUnitRole.DeleteCustomRole:input_type -> api.DeleteCustomRoleReq 4, // 12: api.OrgUnitRole.ListOrgUnitRoles:output_type -> api.OrgUnitRolesListResp - 8, // 13: api.OrgUnitRole.CreateCustomRole:output_type -> api.CreateCustomRoleResp - 10, // 14: api.OrgUnitRole.UpdateCustomRole:output_type -> api.UpdateCustomRoleResp - 12, // 15: api.OrgUnitRole.GetCustomRole:output_type -> api.GetCustomRoleResp - 14, // 16: api.OrgUnitRole.DeleteCustomRole:output_type -> api.DeleteCustomRoleResp + 10, // 13: api.OrgUnitRole.CreateCustomRole:output_type -> api.CreateCustomRoleResp + 12, // 14: api.OrgUnitRole.UpdateCustomRole:output_type -> api.UpdateCustomRoleResp + 14, // 15: api.OrgUnitRole.GetCustomRole:output_type -> api.GetCustomRoleResp + 16, // 16: api.OrgUnitRole.DeleteCustomRole:output_type -> api.DeleteCustomRoleResp 12, // [12:17] is the sub-list for method output_type 7, // [7:12] is the sub-list for method input_type 7, // [7:7] is the sub-list for extension type_name @@ -1125,7 +1148,7 @@ func file_org_unit_role_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_org_unit_role_proto_rawDesc), len(file_org_unit_role_proto_rawDesc)), NumEnums: 2, - NumMessages: 13, + NumMessages: 15, NumExtensions: 0, NumServices: 1, }, diff --git a/api/org-unit-role.proto b/api/org-unit-role.proto index cdcd972..c740995 100644 --- a/api/org-unit-role.proto +++ b/api/org-unit-role.proto @@ -91,14 +91,11 @@ message OrgUnitRolesListEntry { // role type: "built-in" for system roles (admin, auditor) or "custom" for user-defined roles string type = 3; - // display name for custom roles (only applicable for custom roles) - string displayName = 4; - // creation timestamp for custom roles (only applicable for custom roles) - int64 created = 5; + int64 created = 4; // user who created the role for custom roles (only applicable for custom roles) - string createdBy = 6; + string createdBy = 5; } // org unit roles list response - includes both built-in and custom roles @@ -112,35 +109,41 @@ message OrgUnitRolesListResp { } // Matching criteria types for resource-based permissions -enum ResourceMatchCriteria { - // Unspecified/default (treated as wildcard) - RESOURCE_MATCH_CRITERIA_UNSPECIFIED = 0; - // Exact resource name match - RESOURCE_MATCH_CRITERIA_EXACT = 1; - // Resource name starts with the key - RESOURCE_MATCH_CRITERIA_PREFIX = 2; - // Resource name ends with the key - RESOURCE_MATCH_CRITERIA_SUFFIX = 3; - // Resource name matches the regex pattern in key - RESOURCE_MATCH_CRITERIA_REGEX = 4; - // Key uses wildcard matching with * (e.g., "bucket-*", "*-prod", "*") - RESOURCE_MATCH_CRITERIA_WILDCARD = 5; +message ResourceMatchCriteriaDefs { + enum Criteria { + // Unspecified/default (treated as deny for security) + Unspecified = 0; + // Matches all resources (wildcard) + Any = 1; + // Exact resource name match + Exact = 2; + // Resource name starts with the pattern + Prefix = 3; + // Resource name ends with the pattern + Suffix = 4; + // Resource name matches the regex pattern + Regex = 5; + } } // Action types for role permissions -enum RolePermissionAction { - // Unspecified action (invalid) - ROLE_PERMISSION_ACTION_UNSPECIFIED = 0; - // Allow the specified verbs on the resource - ROLE_PERMISSION_ACTION_ALLOW = 1; - // Deny the specified verbs on the resource (takes precedence over Allow) - ROLE_PERMISSION_ACTION_DENY = 2; +message RolePermissionActionDefs { + enum Action { + // Unspecified action (invalid, will be denied) + Unspecified = 0; + // Allow the specified verbs on the resource + Allow = 1; + // Deny the specified verbs on the resource (takes precedence over Allow) + Deny = 2; + // Log the specified verbs on the resource (allows access but logs for audit) + Log = 3; + } } // Resource matching criteria for fine-grained permission control message ResourceMatch { // Matching criteria type - ResourceMatchCriteria criteria = 1; + ResourceMatchCriteriaDefs.Criteria criteria = 1; // The matching key/pattern based on criteria // For wildcard: supports * (e.g., "bucket-*", "*-prod", "*") @@ -160,8 +163,8 @@ message RolePermission { // List of allowed verbs/actions for this resource (supports "*" for all verbs) repeated string verbs = 3; - // Action type: Allow or Deny (Deny takes precedence over Allow) - RolePermissionAction action = 4; + // Action type: Allow, Deny, or Log + RolePermissionActionDefs.Action action = 4; } // Create custom role request @@ -172,20 +175,15 @@ message CreateCustomRoleReq { // Name of the custom role (must be unique within the org unit) string name = 2; - // Display name for the custom role - string displayName = 3; - // Description explaining the purpose of this custom role - string description = 4; + string description = 3; // List of permissions granted by this custom role - repeated RolePermission permissions = 5; + repeated RolePermission permissions = 4; } // Create custom role response message CreateCustomRoleResp { - // Confirmation message - string message = 1; } // Update custom role request @@ -196,20 +194,15 @@ message UpdateCustomRoleReq { // Name of the custom role to update string name = 2; - // Updated display name for the custom role - string displayName = 3; - // Updated description explaining the purpose of this custom role - string description = 4; + string description = 3; // Updated list of permissions granted by this custom role - repeated RolePermission permissions = 5; + repeated RolePermission permissions = 4; } // Update custom role response message UpdateCustomRoleResp { - // Confirmation message - string message = 1; } // Get custom role request @@ -226,26 +219,23 @@ message GetCustomRoleResp { // Name of the custom role string name = 1; - // Display name for the custom role - string displayName = 2; - // Description explaining the purpose of this custom role - string description = 3; + string description = 2; // List of permissions granted by this custom role - repeated RolePermission permissions = 4; + repeated RolePermission permissions = 3; // Timestamp when the role was created - int64 created = 5; + int64 created = 4; // User who created this custom role - string createdBy = 6; + string createdBy = 5; // Timestamp when the role was last updated - int64 updated = 7; + int64 updated = 6; // User who last updated this custom role - string updatedBy = 8; + string updatedBy = 7; } // Delete custom role request diff --git a/api/swagger/apidocs.swagger.json b/api/swagger/apidocs.swagger.json index 1a50dab..162bf95 100644 --- a/api/swagger/apidocs.swagger.json +++ b/api/swagger/apidocs.swagger.json @@ -2511,10 +2511,6 @@ "type": "string", "title": "Name of the custom role (must be unique within the org unit)" }, - "displayName": { - "type": "string", - "title": "Display name for the custom role" - }, "description": { "type": "string", "title": "Description explaining the purpose of this custom role" @@ -2533,10 +2529,6 @@ "OrgUnitRoleUpdateCustomRoleBody": { "type": "object", "properties": { - "displayName": { - "type": "string", - "title": "Updated display name for the custom role" - }, "description": { "type": "string", "title": "Updated description explaining the purpose of this custom role" @@ -2590,6 +2582,30 @@ }, "title": "org unit user role update request" }, + "ResourceMatchCriteriaDefsCriteria": { + "type": "string", + "enum": [ + "Unspecified", + "Any", + "Exact", + "Prefix", + "Suffix", + "Regex" + ], + "default": "Unspecified", + "title": "- Unspecified: Unspecified/default (treated as deny for security)\n - Any: Matches all resources (wildcard)\n - Exact: Exact resource name match\n - Prefix: Resource name starts with the pattern\n - Suffix: Resource name ends with the pattern\n - Regex: Resource name matches the regex pattern" + }, + "RolePermissionActionDefsAction": { + "type": "string", + "enum": [ + "Unspecified", + "Allow", + "Deny", + "Log" + ], + "default": "Unspecified", + "title": "- Unspecified: Unspecified action (invalid, will be denied)\n - Allow: Allow the specified verbs on the resource\n - Deny: Deny the specified verbs on the resource (takes precedence over Allow)\n - Log: Log the specified verbs on the resource (allows access but logs for audit)" + }, "apiApiKeyCreateReq": { "type": "object", "properties": { @@ -2705,12 +2721,6 @@ }, "apiCreateCustomRoleResp": { "type": "object", - "properties": { - "message": { - "type": "string", - "title": "Confirmation message" - } - }, "title": "Create custom role response" }, "apiCustomerAddReq": { @@ -2837,10 +2847,6 @@ "type": "string", "title": "Name of the custom role" }, - "displayName": { - "type": "string", - "title": "Display name for the custom role" - }, "description": { "type": "string", "title": "Description explaining the purpose of this custom role" @@ -3422,10 +3428,6 @@ "type": "string", "title": "role type: \"built-in\" for system roles (admin, auditor) or \"custom\" for user-defined roles" }, - "displayName": { - "type": "string", - "title": "display name for custom roles (only applicable for custom roles)" - }, "created": { "type": "string", "format": "int64", @@ -3608,7 +3610,7 @@ "type": "object", "properties": { "criteria": { - "$ref": "#/definitions/apiResourceMatchCriteria", + "$ref": "#/definitions/ResourceMatchCriteriaDefsCriteria", "title": "Matching criteria type" }, "key": { @@ -3618,20 +3620,6 @@ }, "title": "Resource matching criteria for fine-grained permission control" }, - "apiResourceMatchCriteria": { - "type": "string", - "enum": [ - "RESOURCE_MATCH_CRITERIA_UNSPECIFIED", - "RESOURCE_MATCH_CRITERIA_EXACT", - "RESOURCE_MATCH_CRITERIA_PREFIX", - "RESOURCE_MATCH_CRITERIA_SUFFIX", - "RESOURCE_MATCH_CRITERIA_REGEX", - "RESOURCE_MATCH_CRITERIA_WILDCARD" - ], - "default": "RESOURCE_MATCH_CRITERIA_UNSPECIFIED", - "description": "- RESOURCE_MATCH_CRITERIA_UNSPECIFIED: Unspecified/default (treated as wildcard)\n - RESOURCE_MATCH_CRITERIA_EXACT: Exact resource name match\n - RESOURCE_MATCH_CRITERIA_PREFIX: Resource name starts with the key\n - RESOURCE_MATCH_CRITERIA_SUFFIX: Resource name ends with the key\n - RESOURCE_MATCH_CRITERIA_REGEX: Resource name matches the regex pattern in key\n - RESOURCE_MATCH_CRITERIA_WILDCARD: Key uses wildcard matching with * (e.g., \"bucket-*\", \"*-prod\", \"*\")", - "title": "Matching criteria types for resource-based permissions" - }, "apiRolePermission": { "type": "object", "properties": { @@ -3651,23 +3639,12 @@ "title": "List of allowed verbs/actions for this resource (supports \"*\" for all verbs)" }, "action": { - "$ref": "#/definitions/apiRolePermissionAction", - "title": "Action type: Allow or Deny (Deny takes precedence over Allow)" + "$ref": "#/definitions/RolePermissionActionDefsAction", + "title": "Action type: Allow, Deny, or Log" } }, "title": "Permission definition for custom roles" }, - "apiRolePermissionAction": { - "type": "string", - "enum": [ - "ROLE_PERMISSION_ACTION_UNSPECIFIED", - "ROLE_PERMISSION_ACTION_ALLOW", - "ROLE_PERMISSION_ACTION_DENY" - ], - "default": "ROLE_PERMISSION_ACTION_UNSPECIFIED", - "description": "- ROLE_PERMISSION_ACTION_UNSPECIFIED: Unspecified action (invalid)\n - ROLE_PERMISSION_ACTION_ALLOW: Allow the specified verbs on the resource\n - ROLE_PERMISSION_ACTION_DENY: Deny the specified verbs on the resource (takes precedence over Allow)", - "title": "Action types for role permissions" - }, "apiTenantAdminCreateConfig": { "type": "object", "properties": { @@ -4107,12 +4084,6 @@ }, "apiUpdateCustomRoleResp": { "type": "object", - "properties": { - "message": { - "type": "string", - "title": "Confirmation message" - } - }, "title": "Update custom role response" }, "apiUserCreateReq": { diff --git a/pkg/gateway/server.go b/pkg/gateway/server.go index 7acfc61..603319d 100644 --- a/pkg/gateway/server.go +++ b/pkg/gateway/server.go @@ -246,14 +246,24 @@ func (s *gateway) performOrgUnitRoleCheck(authInfo *common.AuthInfo, ou string, } // Evaluate permissions - return s.evaluateCustomRolePermissions(customRole.Permissions, resource, verb, resourceInstance) + allowed, shouldLog := s.evaluateCustomRolePermissions(customRole.Permissions, resource, verb, resourceInstance) + + // Log entry if Log action was matched + if shouldLog { + log.Printf("[LOG] User: %s, Tenant: %s, OrgUnit: %s, Role: %s, Resource: %s, Verb: %s, Instance: %s, Method: %s, Path: %s, Allowed: %v", + authInfo.UserName, authInfo.Realm, ou, ouUser.Role, resource, verb, resourceInstance, r.Method, r.URL.Path, allowed) + } + + return allowed } // evaluateCustomRolePermissions checks if the custom role's permissions allow the requested resource and verb // For non-list operations, resourceInstance is checked against the permission's match criteria -func (s *gateway) evaluateCustomRolePermissions(permissions []*table.RolePermission, resource, verb, resourceInstance string) bool { +// Returns (allowed bool, shouldLog bool) +func (s *gateway) evaluateCustomRolePermissions(permissions []*table.RolePermission, resource, verb, resourceInstance string) (bool, bool) { var allowMatched bool var denyMatched bool + var logMatched bool for _, perm := range permissions { // Check if resource matches @@ -282,22 +292,26 @@ func (s *gateway) evaluateCustomRolePermissions(permissions []*table.RolePermiss } } - // Apply action (Deny takes precedence) + // Apply action (Deny takes precedence, Log enables audit logging) switch perm.Action { case table.RolePermissionActionDeny: denyMatched = true + case table.RolePermissionActionLog: + // Log action allows access but marks it for audit logging + logMatched = true + allowMatched = true case table.RolePermissionActionAllow, table.RolePermissionActionUnspecified: // Treat UNSPECIFIED or empty as Allow (default permissive behavior) allowMatched = true } } - // Deny takes precedence over Allow + // Deny takes precedence over Allow and Log if denyMatched { - return false + return false, false } - return allowMatched + return allowMatched, logMatched } // matchesResource checks if a requested resource matches the permission's resource pattern diff --git a/pkg/server/org-unit-role.go b/pkg/server/org-unit-role.go index bece257..c0371a0 100644 --- a/pkg/server/org-unit-role.go +++ b/pkg/server/org-unit-role.go @@ -36,20 +36,18 @@ func (s *OrgUnitRoleServer) ListOrgUnitRoles(ctx context.Context, req *api.OrgUn items := []*api.OrgUnitRolesListEntry{ { - Name: "admin", - Desc: "Administrator role to provide access to everything in the Organization Unit including management of users and resources", - Type: "built-in", - DisplayName: "Administrator", - Created: 0, - CreatedBy: "", + Name: "admin", + Desc: "Administrator role to provide access to everything in the Organization Unit including management of users and resources", + Type: "built-in", + Created: 0, + CreatedBy: "", }, { - Name: "auditor", - Desc: "Auditor role to provide read-only access to all the resources available in the Organization Unit", - Type: "built-in", - DisplayName: "Auditor", - Created: 0, - CreatedBy: "", + Name: "auditor", + Desc: "Auditor role to provide read-only access to all the resources available in the Organization Unit", + Type: "built-in", + Created: 0, + CreatedBy: "", }, } @@ -60,12 +58,11 @@ func (s *OrgUnitRoleServer) ListOrgUnitRoles(ctx context.Context, req *api.OrgUn } else { for _, role := range customRoles { items = append(items, &api.OrgUnitRolesListEntry{ - Name: role.Key.Name, - Desc: role.Description, - Type: "custom", - DisplayName: role.DisplayName, - Created: role.Created, - CreatedBy: role.CreatedBy, + Name: role.Key.Name, + Desc: role.Description, + Type: "custom", + Created: role.Created, + CreatedBy: role.CreatedBy, }) } } @@ -137,7 +134,6 @@ func (s *OrgUnitRoleServer) CreateCustomRole(ctx context.Context, req *api.Creat OrgUnitId: req.Ou, Name: req.Name, }, - DisplayName: req.DisplayName, Description: req.Description, Permissions: permissions, Created: time.Now().Unix(), @@ -155,9 +151,7 @@ func (s *OrgUnitRoleServer) CreateCustomRole(ctx context.Context, req *api.Creat return nil, status.Errorf(codes.Internal, "Something went wrong, please try again later") } - return &api.CreateCustomRoleResp{ - Message: "Custom role created successfully", - }, nil + return &api.CreateCustomRoleResp{}, nil } // UpdateCustomRole updates an existing custom role @@ -174,7 +168,6 @@ func (s *OrgUnitRoleServer) UpdateCustomRole(ctx context.Context, req *api.Updat permissions := s.convertProtoPermissionsToTable(req.Permissions) updateRole := &table.OrgUnitCustomRole{ - DisplayName: req.DisplayName, Description: req.Description, Permissions: permissions, Updated: time.Now().Unix(), @@ -196,9 +189,7 @@ func (s *OrgUnitRoleServer) UpdateCustomRole(ctx context.Context, req *api.Updat return nil, status.Errorf(codes.Internal, "Something went wrong, please try again later") } - return &api.UpdateCustomRoleResp{ - Message: "Custom role updated successfully", - }, nil + return &api.UpdateCustomRoleResp{}, nil } // GetCustomRole retrieves details of a specific custom role @@ -221,7 +212,6 @@ func (s *OrgUnitRoleServer) GetCustomRole(ctx context.Context, req *api.GetCusto return &api.GetCustomRoleResp{ Name: customRole.Key.Name, - DisplayName: customRole.DisplayName, Description: customRole.Description, Permissions: permissions, Created: customRole.Created, @@ -259,70 +249,83 @@ func (s *OrgUnitRoleServer) DeleteCustomRole(ctx context.Context, req *api.Delet } // convertProtoResourceMatchCriteriaToTable converts protobuf enum to table string constant -func (s *OrgUnitRoleServer) convertProtoResourceMatchCriteriaToTable(criteria api.ResourceMatchCriteria) table.ResourceMatchCriteria { +func (s *OrgUnitRoleServer) convertProtoResourceMatchCriteriaToTable(criteria api.ResourceMatchCriteriaDefs_Criteria) table.ResourceMatchCriteria { switch criteria { - case api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_EXACT: + case api.ResourceMatchCriteriaDefs_Any: + return table.ResourceMatchCriteriaWildcard + case api.ResourceMatchCriteriaDefs_Exact: return table.ResourceMatchCriteriaExact - case api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_PREFIX: + case api.ResourceMatchCriteriaDefs_Prefix: return table.ResourceMatchCriteriaPrefix - case api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_SUFFIX: + case api.ResourceMatchCriteriaDefs_Suffix: return table.ResourceMatchCriteriaSuffix - case api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_REGEX: + case api.ResourceMatchCriteriaDefs_Regex: return table.ResourceMatchCriteriaRegex - case api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_WILDCARD: - return table.ResourceMatchCriteriaWildcard - case api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_UNSPECIFIED: - fallthrough default: - return table.ResourceMatchCriteriaUnspecified + // Unknown/Unspecified criteria - default to Wildcard (show all) + log.Printf("Unknown or unspecified resource match criteria: %v, defaulting to Wildcard (show all)", criteria) + return table.ResourceMatchCriteriaWildcard } } // convertTableResourceMatchCriteriaToProto converts table string constant to protobuf enum -func (s *OrgUnitRoleServer) convertTableResourceMatchCriteriaToProto(criteria table.ResourceMatchCriteria) api.ResourceMatchCriteria { +func (s *OrgUnitRoleServer) convertTableResourceMatchCriteriaToProto(criteria table.ResourceMatchCriteria) api.ResourceMatchCriteriaDefs_Criteria { switch criteria { case table.ResourceMatchCriteriaExact: - return api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_EXACT + return api.ResourceMatchCriteriaDefs_Exact case table.ResourceMatchCriteriaPrefix: - return api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_PREFIX + return api.ResourceMatchCriteriaDefs_Prefix case table.ResourceMatchCriteriaSuffix: - return api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_SUFFIX + return api.ResourceMatchCriteriaDefs_Suffix case table.ResourceMatchCriteriaRegex: - return api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_REGEX + return api.ResourceMatchCriteriaDefs_Regex case table.ResourceMatchCriteriaWildcard: - return api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_WILDCARD + return api.ResourceMatchCriteriaDefs_Any case table.ResourceMatchCriteriaUnspecified: - fallthrough + // UNSPECIFIED in database - default to Any (show all) + log.Printf("Unspecified resource match criteria in database, defaulting to Any") + return api.ResourceMatchCriteriaDefs_Any default: - return api.ResourceMatchCriteria_RESOURCE_MATCH_CRITERIA_UNSPECIFIED + log.Printf("Unknown table resource match criteria: %v, defaulting to Any", criteria) + return api.ResourceMatchCriteriaDefs_Any } } // convertProtoActionToTable converts protobuf enum to table string constant -func (s *OrgUnitRoleServer) convertProtoActionToTable(action api.RolePermissionAction) table.RolePermissionAction { +func (s *OrgUnitRoleServer) convertProtoActionToTable(action api.RolePermissionActionDefs_Action) table.RolePermissionAction { switch action { - case api.RolePermissionAction_ROLE_PERMISSION_ACTION_ALLOW: + case api.RolePermissionActionDefs_Allow: return table.RolePermissionActionAllow - case api.RolePermissionAction_ROLE_PERMISSION_ACTION_DENY: + case api.RolePermissionActionDefs_Deny: + return table.RolePermissionActionDeny + case api.RolePermissionActionDefs_Log: + return table.RolePermissionActionLog + case api.RolePermissionActionDefs_Unspecified: + // UNSPECIFIED action - default to Deny for security + log.Printf("Unspecified action encountered, defaulting to Deny") return table.RolePermissionActionDeny - case api.RolePermissionAction_ROLE_PERMISSION_ACTION_UNSPECIFIED: - fallthrough default: - return table.RolePermissionActionUnspecified + log.Printf("Unknown action: %v, defaulting to Deny", action) + return table.RolePermissionActionDeny } } // convertTableActionToProto converts table string constant to protobuf enum -func (s *OrgUnitRoleServer) convertTableActionToProto(action table.RolePermissionAction) api.RolePermissionAction { +func (s *OrgUnitRoleServer) convertTableActionToProto(action table.RolePermissionAction) api.RolePermissionActionDefs_Action { switch action { case table.RolePermissionActionAllow: - return api.RolePermissionAction_ROLE_PERMISSION_ACTION_ALLOW + return api.RolePermissionActionDefs_Allow case table.RolePermissionActionDeny: - return api.RolePermissionAction_ROLE_PERMISSION_ACTION_DENY + return api.RolePermissionActionDefs_Deny + case table.RolePermissionActionLog: + return api.RolePermissionActionDefs_Log case table.RolePermissionActionUnspecified: - fallthrough + // UNSPECIFIED in database - default to Deny for security + log.Printf("Unspecified action in database, defaulting to Deny") + return api.RolePermissionActionDefs_Deny default: - return api.RolePermissionAction_ROLE_PERMISSION_ACTION_UNSPECIFIED + log.Printf("Unknown table action: %v, defaulting to Deny", action) + return api.RolePermissionActionDefs_Deny } } @@ -382,13 +385,6 @@ func (s *OrgUnitRoleServer) validateCreateCustomRoleRequest(req *api.CreateCusto return errors.New("role name cannot exceed 50 characters") } - if req.DisplayName == "" { - return errors.New("display name cannot be empty") - } - if len(req.DisplayName) > 100 { - return errors.New("display name cannot exceed 100 characters") - } - // Description is optional, but if provided, validate length if len(req.Description) > 500 { return errors.New("description cannot exceed 500 characters") @@ -413,9 +409,12 @@ func (s *OrgUnitRoleServer) validateCreateCustomRoleRequest(req *api.CreateCusto return fmt.Errorf("permission %d, verb %d: verb cannot be empty", i+1, j+1) } } - if perm.Action != api.RolePermissionAction_ROLE_PERMISSION_ACTION_UNSPECIFIED && - perm.Action != api.RolePermissionAction_ROLE_PERMISSION_ACTION_ALLOW && - perm.Action != api.RolePermissionAction_ROLE_PERMISSION_ACTION_DENY { + if perm.Action == api.RolePermissionActionDefs_Unspecified { + return fmt.Errorf("permission %d: action must be specified (Allow, Deny, or Log)", i+1) + } + if perm.Action != api.RolePermissionActionDefs_Allow && + perm.Action != api.RolePermissionActionDefs_Deny && + perm.Action != api.RolePermissionActionDefs_Log { return fmt.Errorf("permission %d: invalid action value: %v", i+1, perm.Action) } } @@ -425,13 +424,6 @@ func (s *OrgUnitRoleServer) validateCreateCustomRoleRequest(req *api.CreateCusto // validateUpdateCustomRoleRequest validates the UpdateCustomRole request fields func (s *OrgUnitRoleServer) validateUpdateCustomRoleRequest(req *api.UpdateCustomRoleReq) error { - if req.DisplayName == "" { - return errors.New("display name cannot be empty") - } - if len(req.DisplayName) > 100 { - return errors.New("display name cannot exceed 100 characters") - } - // Description is optional, but if provided, validate length if len(req.Description) > 500 { return errors.New("description cannot exceed 500 characters") @@ -456,9 +448,12 @@ func (s *OrgUnitRoleServer) validateUpdateCustomRoleRequest(req *api.UpdateCusto return fmt.Errorf("permission %d, verb %d: verb cannot be empty", i+1, j+1) } } - if perm.Action != api.RolePermissionAction_ROLE_PERMISSION_ACTION_UNSPECIFIED && - perm.Action != api.RolePermissionAction_ROLE_PERMISSION_ACTION_ALLOW && - perm.Action != api.RolePermissionAction_ROLE_PERMISSION_ACTION_DENY { + if perm.Action == api.RolePermissionActionDefs_Unspecified { + return fmt.Errorf("permission %d: action must be specified (Allow, Deny, or Log)", i+1) + } + if perm.Action != api.RolePermissionActionDefs_Allow && + perm.Action != api.RolePermissionActionDefs_Deny && + perm.Action != api.RolePermissionActionDefs_Log { return fmt.Errorf("permission %d: invalid action value: %v", i+1, perm.Action) } } diff --git a/pkg/table/org-unit-role.go b/pkg/table/org-unit-role.go index 9084f65..9f5967b 100644 --- a/pkg/table/org-unit-role.go +++ b/pkg/table/org-unit-role.go @@ -52,6 +52,7 @@ const ( RolePermissionActionUnspecified RolePermissionAction = "" RolePermissionActionAllow RolePermissionAction = "Allow" RolePermissionActionDeny RolePermissionAction = "Deny" + RolePermissionActionLog RolePermissionAction = "Log" ) // RolePermission defines individual permission for a resource @@ -70,8 +71,6 @@ type RolePermission struct { type OrgUnitCustomRole struct { // Custom role key Key *OrgUnitCustomRoleKey `bson:"key,omitempty"` - // Display name for the custom role - DisplayName string `bson:"displayName,omitempty"` // Description explaining the purpose of this custom role Description string `bson:"description,omitempty"` // List of permissions granted by this custom role