diff --git a/generate/generate.go b/generate/generate.go index 29e4970d..8728c5b4 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -105,7 +105,8 @@ func servicePermissions(root string) (*permissions.ServicePermissions, error) { serverReflectionInfov1alpha1: true, serverReflectionInfo: true, } - visibility = permissions.Visibility{ + methodRoles = permissions.MethodRoles{} + visibility = permissions.Visibility{ Public: map[string]bool{ // Allow service reflection to list available methods serverReflectionInfov1alpha1: true, @@ -136,6 +137,7 @@ func servicePermissions(root string) (*permissions.ServicePermissions, error) { for _, method := range serviceDesc.GetMethod() { methodName := fmt.Sprintf("/%s.%s/%s", *fd.Package, *serviceDesc.Name, *method.Name) methodOpts := method.Options.GetUninterpretedOption() + methodRoles[methodName] = []string{} for _, methodOpt := range methodOpts { for _, namePart := range methodOpt.Name { if !*namePart.IsExtension { @@ -143,49 +145,60 @@ func servicePermissions(root string) (*permissions.ServicePermissions, error) { } auditable[methodName] = true // Tenant - switch *methodOpt.IdentifierValue { + switch role := *methodOpt.IdentifierValue; role { case v1.TenantRole_TENANT_ROLE_OWNER.String(): - roles.Tenant[v1.TenantRole_TENANT_ROLE_OWNER.String()] = append(roles.Tenant[v1.TenantRole_TENANT_ROLE_OWNER.String()], methodName) + roles.Tenant[role] = append(roles.Tenant[role], methodName) visibility.Tenant[methodName] = true + methodRoles[methodName] = append(methodRoles[methodName], role) case v1.TenantRole_TENANT_ROLE_EDITOR.String(): - roles.Tenant[v1.TenantRole_TENANT_ROLE_EDITOR.String()] = append(roles.Tenant[v1.TenantRole_TENANT_ROLE_EDITOR.String()], methodName) + roles.Tenant[role] = append(roles.Tenant[role], methodName) visibility.Tenant[methodName] = true + methodRoles[methodName] = append(methodRoles[methodName], role) case v1.TenantRole_TENANT_ROLE_VIEWER.String(): - roles.Tenant[v1.TenantRole_TENANT_ROLE_VIEWER.String()] = append(roles.Tenant[v1.TenantRole_TENANT_ROLE_VIEWER.String()], methodName) + roles.Tenant[role] = append(roles.Tenant[role], methodName) visibility.Tenant[methodName] = true + methodRoles[methodName] = append(methodRoles[methodName], role) case v1.TenantRole_TENANT_ROLE_GUEST.String(): - roles.Tenant[v1.TenantRole_TENANT_ROLE_GUEST.String()] = append(roles.Tenant[v1.TenantRole_TENANT_ROLE_GUEST.String()], methodName) + roles.Tenant[role] = append(roles.Tenant[role], methodName) visibility.Tenant[methodName] = true + methodRoles[methodName] = append(methodRoles[methodName], role) case v1.TenantRole_TENANT_ROLE_UNSPECIFIED.String(): // noop // Project case v1.ProjectRole_PROJECT_ROLE_OWNER.String(): - roles.Project[v1.ProjectRole_PROJECT_ROLE_OWNER.String()] = append(roles.Project[v1.ProjectRole_PROJECT_ROLE_OWNER.String()], methodName) + roles.Project[role] = append(roles.Project[role], methodName) visibility.Project[methodName] = true + methodRoles[methodName] = append(methodRoles[methodName], role) case v1.ProjectRole_PROJECT_ROLE_EDITOR.String(): visibility.Project[methodName] = true - roles.Project[v1.ProjectRole_PROJECT_ROLE_EDITOR.String()] = append(roles.Project[v1.ProjectRole_PROJECT_ROLE_EDITOR.String()], methodName) + roles.Project[role] = append(roles.Project[role], methodName) + methodRoles[methodName] = append(methodRoles[methodName], role) case v1.ProjectRole_PROJECT_ROLE_VIEWER.String(): visibility.Project[methodName] = true - roles.Project[v1.ProjectRole_PROJECT_ROLE_VIEWER.String()] = append(roles.Project[v1.ProjectRole_PROJECT_ROLE_VIEWER.String()], methodName) + roles.Project[role] = append(roles.Project[role], methodName) + methodRoles[methodName] = append(methodRoles[methodName], role) case v1.ProjectRole_PROJECT_ROLE_UNSPECIFIED.String(): // noop // Admin case v1.AdminRole_ADMIN_ROLE_EDITOR.String(): - roles.Admin[v1.AdminRole_ADMIN_ROLE_EDITOR.String()] = append(roles.Admin[v1.AdminRole_ADMIN_ROLE_EDITOR.String()], methodName) + roles.Admin[role] = append(roles.Admin[role], methodName) visibility.Admin[methodName] = true + methodRoles[methodName] = append(methodRoles[methodName], role) case v1.AdminRole_ADMIN_ROLE_VIEWER.String(): - roles.Admin[v1.AdminRole_ADMIN_ROLE_VIEWER.String()] = append(roles.Admin[v1.AdminRole_ADMIN_ROLE_VIEWER.String()], methodName) + roles.Admin[role] = append(roles.Admin[role], methodName) visibility.Admin[methodName] = true + methodRoles[methodName] = append(methodRoles[methodName], role) case v1.AdminRole_ADMIN_ROLE_UNSPECIFIED.String(): // noop // Infra case v1.InfraRole_INFRA_ROLE_EDITOR.String(): - roles.Infra[v1.InfraRole_INFRA_ROLE_EDITOR.String()] = append(roles.Infra[v1.InfraRole_INFRA_ROLE_EDITOR.String()], methodName) + roles.Infra[role] = append(roles.Infra[role], methodName) visibility.Infra[methodName] = true + methodRoles[methodName] = append(methodRoles[methodName], role) case v1.InfraRole_INFRA_ROLE_VIEWER.String(): - roles.Infra[v1.InfraRole_INFRA_ROLE_VIEWER.String()] = append(roles.Infra[v1.InfraRole_INFRA_ROLE_VIEWER.String()], methodName) + roles.Infra[role] = append(roles.Infra[role], methodName) visibility.Infra[methodName] = true + methodRoles[methodName] = append(methodRoles[methodName], role) case v1.InfraRole_INFRA_ROLE_UNSPECIFIED.String(): // noop // Visibility @@ -215,11 +228,12 @@ func servicePermissions(root string) (*permissions.ServicePermissions, error) { } slices.Sort(services) sp := &permissions.ServicePermissions{ - Roles: roles, - Methods: methods, - Visibility: visibility, - Auditable: auditable, - Services: services, + Roles: roles, + Methods: methods, + Visibility: visibility, + Auditable: auditable, + Services: services, + MethodRoles: methodRoles, } return sp, nil diff --git a/generate/go_servicepermissions.tpl b/generate/go_servicepermissions.tpl index 9245cb2b..f7cea71d 100644 --- a/generate/go_servicepermissions.tpl +++ b/generate/go_servicepermissions.tpl @@ -56,6 +56,15 @@ func GetServicePermissions() *ServicePermissions { Methods: map[string]bool{ {{- range $key, $value := .Methods }} "{{ $key }}": {{ $value }} , +{{- end }} + }, + MethodRoles: map[string][]string{ +{{- range $method, $roles := .MethodRoles }} + "{{ $method }}": { +{{- range $role := $roles }} + "{{ $role }}" , +{{- end }} + } , {{- end }} }, Visibility: Visibility{ @@ -153,4 +162,4 @@ func GetProjectFromRequest(req connect.AnyRequest) (string, bool) { return rq.GetProject(), true } return "", false -} \ No newline at end of file +} diff --git a/go/permissions/permissions.go b/go/permissions/permissions.go index 40bb83e8..a2c57a69 100644 --- a/go/permissions/permissions.go +++ b/go/permissions/permissions.go @@ -6,14 +6,16 @@ import ( type ( ServicePermissions struct { - Roles Roles `json:"roles"` - Methods Methods `json:"methods"` - Visibility Visibility `json:"visibility"` - Auditable Auditable `json:"auditable,omitempty"` - Services []string `json:"services,omitempty"` + Roles Roles `json:"roles"` + Methods Methods `json:"methods"` + Visibility Visibility `json:"visibility"` + Auditable Auditable `json:"auditable,omitempty"` + Services []string `json:"services,omitempty"` + MethodRoles MethodRoles `json:"methodroles,omitempty"` } - Methods map[string]bool + Methods map[string]bool + MethodRoles map[string][]string Chargeable map[string]bool Auditable map[string]bool @@ -23,7 +25,6 @@ type ( Tenant map[string][]string Project map[string][]string - // Roles Roles struct { Admin Admin `json:"admin,omitempty"` Infra Infra `json:"infra,omitempty"` diff --git a/go/permissions/servicepermissions.go b/go/permissions/servicepermissions.go index 553e2146..3a43611c 100755 --- a/go/permissions/servicepermissions.go +++ b/go/permissions/servicepermissions.go @@ -294,6 +294,300 @@ func GetServicePermissions() *ServicePermissions { "/metalstack.infra.v2.SwitchService/Heartbeat": true, "/metalstack.infra.v2.SwitchService/Register": true, }, + MethodRoles: map[string][]string{ + "/metalstack.admin.v2.FilesystemService/Create": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.FilesystemService/Delete": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.FilesystemService/Update": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.IPService/List": { + "ADMIN_ROLE_EDITOR", + "ADMIN_ROLE_VIEWER", + }, + "/metalstack.admin.v2.ImageService/Create": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.ImageService/Delete": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.ImageService/Update": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.ImageService/Usage": { + "ADMIN_ROLE_EDITOR", + "ADMIN_ROLE_VIEWER", + }, + "/metalstack.admin.v2.MachineService/Get": { + "ADMIN_ROLE_EDITOR", + "ADMIN_ROLE_VIEWER", + }, + "/metalstack.admin.v2.MachineService/List": { + "ADMIN_ROLE_EDITOR", + "ADMIN_ROLE_VIEWER", + }, + "/metalstack.admin.v2.NetworkService/Create": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.NetworkService/Delete": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.NetworkService/Get": { + "ADMIN_ROLE_EDITOR", + "ADMIN_ROLE_VIEWER", + }, + "/metalstack.admin.v2.NetworkService/List": { + "ADMIN_ROLE_EDITOR", + "ADMIN_ROLE_VIEWER", + }, + "/metalstack.admin.v2.NetworkService/Update": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.PartitionService/Capacity": { + "ADMIN_ROLE_VIEWER", + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.PartitionService/Create": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.PartitionService/Delete": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.PartitionService/Update": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.ProjectService/List": { + "ADMIN_ROLE_EDITOR", + "ADMIN_ROLE_VIEWER", + }, + "/metalstack.admin.v2.SizeService/Create": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.SizeService/Delete": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.SizeService/Update": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.SwitchService/Delete": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.SwitchService/Get": { + "ADMIN_ROLE_EDITOR", + "ADMIN_ROLE_VIEWER", + }, + "/metalstack.admin.v2.SwitchService/List": { + "ADMIN_ROLE_EDITOR", + "ADMIN_ROLE_VIEWER", + }, + "/metalstack.admin.v2.SwitchService/Migrate": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.SwitchService/Port": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.SwitchService/Update": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.TenantService/Create": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.TenantService/List": { + "ADMIN_ROLE_EDITOR", + "ADMIN_ROLE_VIEWER", + }, + "/metalstack.admin.v2.TokenService/Create": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.admin.v2.TokenService/List": { + "ADMIN_ROLE_EDITOR", + "ADMIN_ROLE_VIEWER", + }, + "/metalstack.admin.v2.TokenService/Revoke": { + "ADMIN_ROLE_EDITOR", + }, + "/metalstack.api.v2.FilesystemService/Get": {}, + "/metalstack.api.v2.FilesystemService/List": {}, + "/metalstack.api.v2.FilesystemService/Match": {}, + "/metalstack.api.v2.HealthService/Get": {}, + "/metalstack.api.v2.IPService/Create": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + }, + "/metalstack.api.v2.IPService/Delete": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + }, + "/metalstack.api.v2.IPService/Get": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + "PROJECT_ROLE_VIEWER", + }, + "/metalstack.api.v2.IPService/List": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + "PROJECT_ROLE_VIEWER", + }, + "/metalstack.api.v2.IPService/Update": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + }, + "/metalstack.api.v2.ImageService/Get": {}, + "/metalstack.api.v2.ImageService/Latest": {}, + "/metalstack.api.v2.ImageService/List": {}, + "/metalstack.api.v2.MachineService/Create": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + }, + "/metalstack.api.v2.MachineService/Delete": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + }, + "/metalstack.api.v2.MachineService/Get": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + "PROJECT_ROLE_VIEWER", + }, + "/metalstack.api.v2.MachineService/List": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + "PROJECT_ROLE_VIEWER", + }, + "/metalstack.api.v2.MachineService/Update": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + }, + "/metalstack.api.v2.MethodService/List": {}, + "/metalstack.api.v2.MethodService/TokenScopedList": {}, + "/metalstack.api.v2.NetworkService/Create": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + }, + "/metalstack.api.v2.NetworkService/Delete": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + }, + "/metalstack.api.v2.NetworkService/Get": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + "PROJECT_ROLE_VIEWER", + }, + "/metalstack.api.v2.NetworkService/List": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + "PROJECT_ROLE_VIEWER", + }, + "/metalstack.api.v2.NetworkService/ListBaseNetworks": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + "PROJECT_ROLE_VIEWER", + }, + "/metalstack.api.v2.NetworkService/Update": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + }, + "/metalstack.api.v2.PartitionService/Get": {}, + "/metalstack.api.v2.PartitionService/List": {}, + "/metalstack.api.v2.ProjectService/Create": { + "TENANT_ROLE_OWNER", + "TENANT_ROLE_EDITOR", + }, + "/metalstack.api.v2.ProjectService/Delete": { + "PROJECT_ROLE_OWNER", + }, + "/metalstack.api.v2.ProjectService/Get": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + "PROJECT_ROLE_VIEWER", + }, + "/metalstack.api.v2.ProjectService/Invite": { + "PROJECT_ROLE_OWNER", + }, + "/metalstack.api.v2.ProjectService/InviteAccept": {}, + "/metalstack.api.v2.ProjectService/InviteDelete": { + "PROJECT_ROLE_OWNER", + }, + "/metalstack.api.v2.ProjectService/InviteGet": {}, + "/metalstack.api.v2.ProjectService/InvitesList": { + "PROJECT_ROLE_OWNER", + }, + "/metalstack.api.v2.ProjectService/Leave": { + "PROJECT_ROLE_VIEWER", + }, + "/metalstack.api.v2.ProjectService/List": {}, + "/metalstack.api.v2.ProjectService/RemoveMember": { + "PROJECT_ROLE_OWNER", + }, + "/metalstack.api.v2.ProjectService/Update": { + "PROJECT_ROLE_OWNER", + "PROJECT_ROLE_EDITOR", + }, + "/metalstack.api.v2.ProjectService/UpdateMember": { + "PROJECT_ROLE_OWNER", + }, + "/metalstack.api.v2.SizeService/Get": {}, + "/metalstack.api.v2.SizeService/List": {}, + "/metalstack.api.v2.TenantService/Create": {}, + "/metalstack.api.v2.TenantService/Delete": { + "TENANT_ROLE_OWNER", + "TENANT_ROLE_EDITOR", + }, + "/metalstack.api.v2.TenantService/Get": { + "TENANT_ROLE_OWNER", + "TENANT_ROLE_EDITOR", + "TENANT_ROLE_VIEWER", + "TENANT_ROLE_GUEST", + }, + "/metalstack.api.v2.TenantService/Invite": { + "TENANT_ROLE_OWNER", + }, + "/metalstack.api.v2.TenantService/InviteAccept": {}, + "/metalstack.api.v2.TenantService/InviteDelete": { + "TENANT_ROLE_OWNER", + }, + "/metalstack.api.v2.TenantService/InviteGet": {}, + "/metalstack.api.v2.TenantService/InvitesList": { + "TENANT_ROLE_OWNER", + }, + "/metalstack.api.v2.TenantService/Leave": { + "TENANT_ROLE_VIEWER", + }, + "/metalstack.api.v2.TenantService/List": {}, + "/metalstack.api.v2.TenantService/RemoveMember": { + "TENANT_ROLE_OWNER", + }, + "/metalstack.api.v2.TenantService/Update": { + "TENANT_ROLE_OWNER", + "TENANT_ROLE_EDITOR", + }, + "/metalstack.api.v2.TenantService/UpdateMember": { + "TENANT_ROLE_OWNER", + }, + "/metalstack.api.v2.TokenService/Create": {}, + "/metalstack.api.v2.TokenService/Get": {}, + "/metalstack.api.v2.TokenService/List": {}, + "/metalstack.api.v2.TokenService/Refresh": {}, + "/metalstack.api.v2.TokenService/Revoke": {}, + "/metalstack.api.v2.TokenService/Update": {}, + "/metalstack.api.v2.UserService/Get": {}, + "/metalstack.api.v2.VersionService/Get": {}, + "/metalstack.infra.v2.BMCService/UpdateBMCInfo": { + "INFRA_ROLE_EDITOR", + }, + "/metalstack.infra.v2.SwitchService/Get": { + "INFRA_ROLE_EDITOR", + "INFRA_ROLE_VIEWER", + }, + "/metalstack.infra.v2.SwitchService/Heartbeat": { + "INFRA_ROLE_EDITOR", + }, + "/metalstack.infra.v2.SwitchService/Register": { + "INFRA_ROLE_EDITOR", + }, + }, Visibility: Visibility{ Public: map[string]bool{ "/grpc.reflection.v1.ServerReflection/ServerReflectionInfo": true, diff --git a/go/tests/mocks/client/Adminv2.go b/go/tests/mocks/client/Adminv2.go index ae2ac1a0..8ebd1022 100644 --- a/go/tests/mocks/client/Adminv2.go +++ b/go/tests/mocks/client/Adminv2.go @@ -312,6 +312,52 @@ func (_c *Adminv2_Partition_Call) RunAndReturn(run func() adminv2connect.Partiti return _c } +// Project provides a mock function for the type Adminv2 +func (_mock *Adminv2) Project() adminv2connect.ProjectServiceClient { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for Project") + } + + var r0 adminv2connect.ProjectServiceClient + if returnFunc, ok := ret.Get(0).(func() adminv2connect.ProjectServiceClient); ok { + r0 = returnFunc() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(adminv2connect.ProjectServiceClient) + } + } + return r0 +} + +// Adminv2_Project_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Project' +type Adminv2_Project_Call struct { + *mock.Call +} + +// Project is a helper method to define mock.On call +func (_e *Adminv2_Expecter) Project() *Adminv2_Project_Call { + return &Adminv2_Project_Call{Call: _e.mock.On("Project")} +} + +func (_c *Adminv2_Project_Call) Run(run func()) *Adminv2_Project_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Adminv2_Project_Call) Return(projectServiceClient adminv2connect.ProjectServiceClient) *Adminv2_Project_Call { + _c.Call.Return(projectServiceClient) + return _c +} + +func (_c *Adminv2_Project_Call) RunAndReturn(run func() adminv2connect.ProjectServiceClient) *Adminv2_Project_Call { + _c.Call.Return(run) + return _c +} + // Size provides a mock function for the type Adminv2 func (_mock *Adminv2) Size() adminv2connect.SizeServiceClient { ret := _mock.Called()