mirror of
https://github.com/SigNoz/signoz.git
synced 2026-05-13 21:50:31 +01:00
Compare commits
4 Commits
issue_4360
...
platform-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
478430f426 | ||
|
|
43ec9fd039 | ||
|
|
2768d4c9e6 | ||
|
|
6f6f6bc766 |
@@ -66,10 +66,9 @@ func runGenerateAuthz(_ context.Context) error {
|
||||
registry := coretypes.NewRegistry()
|
||||
|
||||
allowedResources := map[string]bool{
|
||||
coretypes.NewResourceRef(coretypes.ResourceServiceAccount).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceMetaResourcesServiceAccount).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceRole).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceMetaResourcesRole).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceServiceAccount).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceRole).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceMetaResourceFactorAPIKey).String(): true,
|
||||
}
|
||||
|
||||
allowedTypes := map[string]bool{}
|
||||
|
||||
@@ -449,6 +449,7 @@ components:
|
||||
- list
|
||||
- assignee
|
||||
- attach
|
||||
- detach
|
||||
type: string
|
||||
AuthtypesRole:
|
||||
properties:
|
||||
@@ -2206,7 +2207,6 @@ components:
|
||||
- role
|
||||
- organization
|
||||
- metaresource
|
||||
- metaresources
|
||||
type: string
|
||||
DashboardtypesDashboard:
|
||||
properties:
|
||||
@@ -9098,9 +9098,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:list
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:list
|
||||
summary: List roles
|
||||
tags:
|
||||
- role
|
||||
@@ -9172,9 +9172,9 @@ paths:
|
||||
description: Not Implemented
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:create
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:create
|
||||
summary: Create role
|
||||
tags:
|
||||
- role
|
||||
@@ -9234,9 +9234,9 @@ paths:
|
||||
description: Not Implemented
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:delete
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:delete
|
||||
summary: Delete role
|
||||
tags:
|
||||
- role
|
||||
@@ -9285,9 +9285,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:read
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:read
|
||||
summary: Get role
|
||||
tags:
|
||||
- role
|
||||
@@ -9351,9 +9351,9 @@ paths:
|
||||
description: Not Implemented
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:update
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:update
|
||||
summary: Patch role
|
||||
tags:
|
||||
- role
|
||||
@@ -9429,9 +9429,9 @@ paths:
|
||||
description: Not Implemented
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:read
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:read
|
||||
summary: Get objects for a role by relation
|
||||
tags:
|
||||
- role
|
||||
@@ -9507,9 +9507,9 @@ paths:
|
||||
description: Not Implemented
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:update
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:update
|
||||
summary: Patch objects for a role by relation
|
||||
tags:
|
||||
- role
|
||||
@@ -10113,9 +10113,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- serviceaccount:read
|
||||
- factor-api-key:list
|
||||
- tokenizer:
|
||||
- serviceaccount:read
|
||||
- factor-api-key:list
|
||||
summary: List service account keys
|
||||
tags:
|
||||
- serviceaccount
|
||||
@@ -10181,9 +10181,11 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- serviceaccount:update
|
||||
- factor-api-key:create
|
||||
- serviceaccount:attach
|
||||
- tokenizer:
|
||||
- serviceaccount:update
|
||||
- factor-api-key:create
|
||||
- serviceaccount:attach
|
||||
summary: Create a service account key
|
||||
tags:
|
||||
- serviceaccount
|
||||
@@ -10236,9 +10238,11 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- serviceaccount:update
|
||||
- factor-api-key:delete
|
||||
- serviceaccount:detach
|
||||
- tokenizer:
|
||||
- serviceaccount:update
|
||||
- factor-api-key:delete
|
||||
- serviceaccount:detach
|
||||
summary: Revoke a service account key
|
||||
tags:
|
||||
- serviceaccount
|
||||
@@ -10301,9 +10305,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- serviceaccount:update
|
||||
- factor-api-key:update
|
||||
- tokenizer:
|
||||
- serviceaccount:update
|
||||
- factor-api-key:update
|
||||
summary: Updates a service account key
|
||||
tags:
|
||||
- serviceaccount
|
||||
@@ -10475,11 +10479,11 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- serviceaccount:attach
|
||||
- role:attach
|
||||
- serviceaccount:detach
|
||||
- role:detach
|
||||
- tokenizer:
|
||||
- serviceaccount:attach
|
||||
- role:attach
|
||||
- serviceaccount:detach
|
||||
- role:detach
|
||||
summary: Delete service account role
|
||||
tags:
|
||||
- serviceaccount
|
||||
|
||||
@@ -7,17 +7,27 @@ type organization
|
||||
|
||||
type user
|
||||
relations
|
||||
define create: [user, serviceaccount, role#assignee]
|
||||
define list: [user, serviceaccount, role#assignee]
|
||||
|
||||
define read: [user, serviceaccount, role#assignee]
|
||||
define update: [user, serviceaccount, role#assignee]
|
||||
define delete: [user, serviceaccount, role#assignee]
|
||||
|
||||
define attach: [user, serviceaccount, role#assignee]
|
||||
define detach: [user, serviceaccount, role#assignee]
|
||||
|
||||
type serviceaccount
|
||||
relations
|
||||
define create: [user, serviceaccount, role#assignee]
|
||||
define list: [user, serviceaccount, role#assignee]
|
||||
|
||||
define read: [user, serviceaccount, role#assignee]
|
||||
define update: [user, serviceaccount, role#assignee]
|
||||
define delete: [user, serviceaccount, role#assignee]
|
||||
|
||||
define attach: [user, serviceaccount, role#assignee]
|
||||
define detach: [user, serviceaccount, role#assignee]
|
||||
|
||||
type anonymous
|
||||
|
||||
@@ -25,25 +35,28 @@ type role
|
||||
relations
|
||||
define assignee: [user, serviceaccount, anonymous]
|
||||
|
||||
define create: [user, serviceaccount, role#assignee]
|
||||
define list: [user, serviceaccount, role#assignee]
|
||||
|
||||
define read: [user, serviceaccount, role#assignee]
|
||||
define update: [user, serviceaccount, role#assignee]
|
||||
define delete: [user, serviceaccount, role#assignee]
|
||||
define attach: [user, serviceaccount, role#assignee]
|
||||
|
||||
type metaresources
|
||||
define attach: [user, serviceaccount, role#assignee]
|
||||
define detach: [user, serviceaccount, role#assignee]
|
||||
|
||||
type metaresource
|
||||
relations
|
||||
define create: [user, serviceaccount, role#assignee]
|
||||
define list: [user, serviceaccount, role#assignee]
|
||||
|
||||
type metaresource
|
||||
relations
|
||||
define read: [user, serviceaccount, anonymous, role#assignee]
|
||||
define update: [user, serviceaccount, role#assignee]
|
||||
define delete: [user, serviceaccount, role#assignee]
|
||||
define read: [user, serviceaccount, anonymous, role#assignee]
|
||||
define update: [user, serviceaccount, role#assignee]
|
||||
define delete: [user, serviceaccount, role#assignee]
|
||||
|
||||
define block: [user, serviceaccount, role#assignee]
|
||||
define block: [user, serviceaccount, role#assignee]
|
||||
|
||||
|
||||
type telemetryresource
|
||||
relations
|
||||
define read: [user, serviceaccount, role#assignee]
|
||||
define read: [user, serviceaccount, role#assignee]
|
||||
@@ -1840,6 +1840,7 @@ export enum AuthtypesRelationDTO {
|
||||
list = 'list',
|
||||
assignee = 'assignee',
|
||||
attach = 'attach',
|
||||
detach = 'detach',
|
||||
}
|
||||
export interface AuthtypesRoleDTO {
|
||||
/**
|
||||
@@ -4161,7 +4162,6 @@ export enum CoretypesTypeDTO {
|
||||
role = 'role',
|
||||
organization = 'organization',
|
||||
metaresource = 'metaresource',
|
||||
metaresources = 'metaresources',
|
||||
}
|
||||
export interface DashboardtypesDashboardDTO {
|
||||
/**
|
||||
|
||||
@@ -360,8 +360,7 @@ describe('createGuardedRoute', () => {
|
||||
const obj = payload[0]?.object;
|
||||
const kind = obj?.resource?.kind;
|
||||
const selector = obj?.selector ?? '*';
|
||||
const objectStr =
|
||||
obj?.resource?.type === 'metaresources' ? kind : `${kind}:${selector}`;
|
||||
const objectStr = `${kind}:${selector}`;
|
||||
requestedObjects.push(objectStr ?? '');
|
||||
|
||||
return res(ctx.status(200), ctx.json(authzMockResponse(payload, [true])));
|
||||
|
||||
@@ -4,12 +4,8 @@ export default {
|
||||
data: {
|
||||
resources: [
|
||||
{
|
||||
kind: 'role',
|
||||
type: 'metaresources',
|
||||
},
|
||||
{
|
||||
kind: 'serviceaccount',
|
||||
type: 'metaresources',
|
||||
kind: 'factor-api-key',
|
||||
type: 'metaresource',
|
||||
},
|
||||
{
|
||||
kind: 'role',
|
||||
@@ -22,12 +18,13 @@ export default {
|
||||
],
|
||||
relations: {
|
||||
assignee: ['role'],
|
||||
attach: ['role', 'serviceaccount'],
|
||||
create: ['metaresources'],
|
||||
delete: ['role', 'serviceaccount'],
|
||||
list: ['metaresources'],
|
||||
read: ['role', 'serviceaccount'],
|
||||
update: ['role', 'serviceaccount'],
|
||||
attach: ['metaresource', 'role', 'serviceaccount'],
|
||||
create: ['metaresource', 'role', 'serviceaccount'],
|
||||
delete: ['metaresource', 'role', 'serviceaccount'],
|
||||
detach: ['metaresource', 'role', 'serviceaccount'],
|
||||
list: ['metaresource', 'role', 'serviceaccount'],
|
||||
read: ['metaresource', 'role', 'serviceaccount'],
|
||||
update: ['metaresource', 'role', 'serviceaccount'],
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
@@ -80,19 +80,6 @@ export function permissionToTransactionDto(
|
||||
permission: BrandedPermission,
|
||||
): AuthtypesTransactionDTO {
|
||||
const { relation, object: objectStr } = parsePermission(permission);
|
||||
const directType = resolveType(relation, objectStr);
|
||||
if (directType === 'metaresources') {
|
||||
return {
|
||||
relation: relation as AuthtypesRelationDTO,
|
||||
object: {
|
||||
resource: {
|
||||
kind: objectStr as ResourceName,
|
||||
type: directType as CoretypesTypeDTO,
|
||||
},
|
||||
selector: '*',
|
||||
},
|
||||
};
|
||||
}
|
||||
const { resourceName, selector } = splitObjectString(objectStr);
|
||||
const type = resolveType(relation, resourceName) ?? 'metaresource';
|
||||
|
||||
@@ -117,9 +104,6 @@ export function gettableTransactionToPermission(
|
||||
} = item;
|
||||
const resourceName = String(resource.kind);
|
||||
const selectorStr = typeof selector === 'string' ? selector : '*';
|
||||
const objectStr =
|
||||
resource.type === 'metaresources'
|
||||
? resourceName
|
||||
: `${resourceName}${ObjectSeparator}${selectorStr}`;
|
||||
const objectStr = `${resourceName}${ObjectSeparator}${selectorStr}`;
|
||||
return `${relation}${PermissionSeparator}${objectStr}` as BrandedPermission;
|
||||
}
|
||||
|
||||
@@ -7,11 +7,14 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/coretypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
if err := router.Handle("/api/v1/roles", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.Create), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles", handler.New(provider.authzMiddleware.Check(provider.authzHandler.Create, authtypes.Relation{Verb: coretypes.VerbCreate}, coretypes.ResourceRole, roleCollectionSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "CreateRole",
|
||||
Tags: []string{"role"},
|
||||
Summary: "Create role",
|
||||
@@ -23,12 +26,14 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusCreated,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbCreate)}),
|
||||
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/roles", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.List), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles", handler.New(provider.authzMiddleware.Check(provider.authzHandler.List, authtypes.Relation{Verb: coretypes.VerbList}, coretypes.ResourceRole, roleCollectionSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "ListRoles",
|
||||
Tags: []string{"role"},
|
||||
Summary: "List roles",
|
||||
@@ -40,12 +45,14 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbList)}),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.Get), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authzMiddleware.Check(provider.authzHandler.Get, authtypes.Relation{Verb: coretypes.VerbRead}, coretypes.ResourceRole, provider.roleInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "GetRole",
|
||||
Tags: []string{"role"},
|
||||
Summary: "Get role",
|
||||
@@ -57,12 +64,14 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbRead)}),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/roles/{id}/relations/{relation}/objects", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.GetObjects), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles/{id}/relations/{relation}/objects", handler.New(provider.authzMiddleware.Check(provider.authzHandler.GetObjects, authtypes.Relation{Verb: coretypes.VerbRead}, coretypes.ResourceRole, provider.roleInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "GetObjects",
|
||||
Tags: []string{"role"},
|
||||
Summary: "Get objects for a role by relation",
|
||||
@@ -74,12 +83,14 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbRead)}),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.Patch), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authzMiddleware.Check(provider.authzHandler.Patch, authtypes.Relation{Verb: coretypes.VerbUpdate}, coretypes.ResourceRole, provider.roleInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "PatchRole",
|
||||
Tags: []string{"role"},
|
||||
Summary: "Patch role",
|
||||
@@ -91,12 +102,14 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbUpdate)}),
|
||||
})).Methods(http.MethodPatch).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/roles/{id}/relations/{relation}/objects", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.PatchObjects), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles/{id}/relations/{relation}/objects", handler.New(provider.authzMiddleware.Check(provider.authzHandler.PatchObjects, authtypes.Relation{Verb: coretypes.VerbUpdate}, coretypes.ResourceRole, provider.roleInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "PatchObjects",
|
||||
Tags: []string{"role"},
|
||||
Summary: "Patch objects for a role by relation",
|
||||
@@ -108,12 +121,14 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusBadRequest, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbUpdate)}),
|
||||
})).Methods(http.MethodPatch).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.Delete), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authzMiddleware.Check(provider.authzHandler.Delete, authtypes.Relation{Verb: coretypes.VerbDelete}, coretypes.ResourceRole, provider.roleInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "DeleteRole",
|
||||
Tags: []string{"role"},
|
||||
Summary: "Delete role",
|
||||
@@ -125,10 +140,33 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbDelete)}),
|
||||
})).Methods(http.MethodDelete).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func roleCollectionSelectorCallback(_ *http.Request, _ authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
return []coretypes.Selector{
|
||||
coretypes.TypeRole.MustSelector(coretypes.WildCardSelectorString),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (provider *provider) roleInstanceSelectorCallback(req *http.Request, claims authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
roleID, err := valuer.NewUUID(mux.Vars(req)["id"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
role, err := provider.authzService.Get(req.Context(), valuer.MustNewUUID(claims.OrgID), roleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []coretypes.Selector{
|
||||
coretypes.TypeRole.MustSelector(role.Name),
|
||||
coretypes.TypeRole.MustSelector(coretypes.WildCardSelectorString),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
if err := router.Handle("/api/v1/service_accounts", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.Create, authtypes.Relation{Verb: coretypes.VerbCreate}, coretypes.ResourceMetaResourcesServiceAccount, serviceAccountCollectionSelectorCallback, []string{
|
||||
if err := router.Handle("/api/v1/service_accounts", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.Create, authtypes.Relation{Verb: coretypes.VerbCreate}, coretypes.ResourceServiceAccount, serviceAccountCollectionSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "CreateServiceAccount",
|
||||
@@ -31,12 +31,12 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusCreated,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourcesServiceAccount.Scope(coretypes.VerbCreate)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbCreate)}),
|
||||
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/service_accounts", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.List, authtypes.Relation{Verb: coretypes.VerbList}, coretypes.ResourceMetaResourcesServiceAccount, serviceAccountCollectionSelectorCallback, []string{
|
||||
if err := router.Handle("/api/v1/service_accounts", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.List, authtypes.Relation{Verb: coretypes.VerbList}, coretypes.ResourceServiceAccount, serviceAccountCollectionSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "ListServiceAccounts",
|
||||
@@ -50,7 +50,7 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourcesServiceAccount.Scope(coretypes.VerbList)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbList)}),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -135,10 +135,10 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/roles/{rid}", handler.New(provider.authzMiddleware.CheckAll(provider.serviceAccountHandler.DeleteRole, []middleware.AuthZCheckGroup{
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbAttach}, Resource: coretypes.ResourceServiceAccount, SelectorCallback: serviceAccountInstanceSelectorCallback, Roles: []string{
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbDetach}, Resource: coretypes.ResourceServiceAccount, SelectorCallback: serviceAccountInstanceSelectorCallback, Roles: []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}}},
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbAttach}, Resource: coretypes.ResourceRole, SelectorCallback: provider.roleAttachSelectorFromPath, Roles: []string{
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbDetach}, Resource: coretypes.ResourceRole, SelectorCallback: provider.roleDetachSelectorFromPath, Roles: []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}}},
|
||||
}), handler.OpenAPIDef{
|
||||
@@ -153,7 +153,7 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbAttach), coretypes.ResourceRole.Scope(coretypes.VerbAttach)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbDetach), coretypes.ResourceRole.Scope(coretypes.VerbDetach)}),
|
||||
})).Methods(http.MethodDelete).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -213,8 +213,13 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.CreateFactorAPIKey, authtypes.Relation{Verb: coretypes.VerbUpdate}, coretypes.ResourceServiceAccount, serviceAccountInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys", handler.New(provider.authzMiddleware.CheckAll(provider.serviceAccountHandler.CreateFactorAPIKey, []middleware.AuthZCheckGroup{
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbCreate}, Resource: coretypes.ResourceMetaResourceFactorAPIKey, SelectorCallback: factorAPIKeyCollectionSelectorCallback, Roles: []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}}},
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbAttach}, Resource: coretypes.ResourceServiceAccount, SelectorCallback: serviceAccountInstanceSelectorCallback, Roles: []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}}},
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "CreateServiceAccountKey",
|
||||
Tags: []string{"serviceaccount"},
|
||||
@@ -227,12 +232,12 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusCreated,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbUpdate)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceFactorAPIKey.Scope(coretypes.VerbCreate), coretypes.ResourceServiceAccount.Scope(coretypes.VerbAttach)}),
|
||||
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.ListFactorAPIKey, authtypes.Relation{Verb: coretypes.VerbRead}, coretypes.ResourceServiceAccount, serviceAccountInstanceSelectorCallback, []string{
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.ListFactorAPIKey, authtypes.Relation{Verb: coretypes.VerbList}, coretypes.ResourceMetaResourceFactorAPIKey, factorAPIKeyCollectionSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "ListServiceAccountKeys",
|
||||
@@ -246,12 +251,12 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbRead)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceFactorAPIKey.Scope(coretypes.VerbList)}),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys/{fid}", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.UpdateFactorAPIKey, authtypes.Relation{Verb: coretypes.VerbUpdate}, coretypes.ResourceServiceAccount, serviceAccountInstanceSelectorCallback, []string{
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys/{fid}", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.UpdateFactorAPIKey, authtypes.Relation{Verb: coretypes.VerbUpdate}, coretypes.ResourceMetaResourceFactorAPIKey, factorAPIKeyInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "UpdateServiceAccountKey",
|
||||
@@ -265,13 +270,18 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbUpdate)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceFactorAPIKey.Scope(coretypes.VerbUpdate)}),
|
||||
})).Methods(http.MethodPut).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys/{fid}", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.RevokeFactorAPIKey, authtypes.Relation{Verb: coretypes.VerbUpdate}, coretypes.ResourceServiceAccount, serviceAccountInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys/{fid}", handler.New(provider.authzMiddleware.CheckAll(provider.serviceAccountHandler.RevokeFactorAPIKey, []middleware.AuthZCheckGroup{
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbDelete}, Resource: coretypes.ResourceMetaResourceFactorAPIKey, SelectorCallback: factorAPIKeyInstanceSelectorCallback, Roles: []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}}},
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbDetach}, Resource: coretypes.ResourceServiceAccount, SelectorCallback: serviceAccountInstanceSelectorCallback, Roles: []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}}},
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "RevokeServiceAccountKey",
|
||||
Tags: []string{"serviceaccount"},
|
||||
@@ -284,7 +294,7 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbUpdate)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceFactorAPIKey.Scope(coretypes.VerbDelete), coretypes.ResourceServiceAccount.Scope(coretypes.VerbDetach)}),
|
||||
})).Methods(http.MethodDelete).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -292,7 +302,7 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *provider) roleAttachSelectorFromPath(req *http.Request, claims authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
func (provider *provider) roleDetachSelectorFromPath(req *http.Request, claims authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
roleID, err := valuer.NewUUID(mux.Vars(req)["rid"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -333,9 +343,28 @@ func (provider *provider) roleAttachSelectorFromBody(req *http.Request, claims a
|
||||
}, nil
|
||||
}
|
||||
|
||||
func factorAPIKeyCollectionSelectorCallback(_ *http.Request, _ authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
return []coretypes.Selector{
|
||||
coretypes.TypeMetaResource.MustSelector(coretypes.WildCardSelectorString),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func factorAPIKeyInstanceSelectorCallback(req *http.Request, _ authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
fid := mux.Vars(req)["fid"]
|
||||
fidSelector, err := coretypes.TypeMetaResource.Selector(fid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []coretypes.Selector{
|
||||
fidSelector,
|
||||
coretypes.TypeMetaResource.MustSelector(coretypes.WildCardSelectorString),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func serviceAccountCollectionSelectorCallback(_ *http.Request, _ authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
return []coretypes.Selector{
|
||||
coretypes.TypeMetaResources.MustSelector(coretypes.WildCardSelectorString),
|
||||
coretypes.TypeServiceAccount.MustSelector(coretypes.WildCardSelectorString),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -200,6 +200,7 @@ func NewSQLMigrationProviderFactories(
|
||||
sqlmigration.NewAddServiceAccountManagedRoleTransactionsFactory(sqlstore),
|
||||
sqlmigration.NewAddSpanMapperFactory(sqlstore, sqlschema),
|
||||
sqlmigration.NewAddLLMPricingRulesFactory(sqlstore, sqlschema),
|
||||
sqlmigration.NewMigrateMetaresourcesTuplesFactory(sqlstore),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
277
pkg/sqlmigration/081_migrate_metaresources_tuples.go
Normal file
277
pkg/sqlmigration/081_migrate_metaresources_tuples.go
Normal file
@@ -0,0 +1,277 @@
|
||||
package sqlmigration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/oklog/ulid/v2"
|
||||
"github.com/uptrace/bun"
|
||||
"github.com/uptrace/bun/dialect"
|
||||
"github.com/uptrace/bun/migrate"
|
||||
)
|
||||
|
||||
type migrateMetaresourcesTuples struct {
|
||||
sqlstore sqlstore.SQLStore
|
||||
}
|
||||
|
||||
func NewMigrateMetaresourcesTuplesFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] {
|
||||
return factory.NewProviderFactory(factory.MustNewName("migrate_metaresources_tuples"), func(ctx context.Context, ps factory.ProviderSettings, c Config) (SQLMigration, error) {
|
||||
return &migrateMetaresourcesTuples{sqlstore: sqlstore}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (migration *migrateMetaresourcesTuples) Register(migrations *migrate.Migrations) error {
|
||||
return migrations.Register(migration.Up, migration.Down)
|
||||
}
|
||||
|
||||
// migrationTuple describes a single FGA tuple to insert.
|
||||
type migrationTuple struct {
|
||||
roleName string // "signoz-admin", "signoz-editor", "signoz-viewer"
|
||||
objectType string // "serviceaccount", "user", "role", "metaresource"
|
||||
objectName string // "serviceaccount", "user", "role", etc.
|
||||
relation string // "create", "list", "detach", etc.
|
||||
}
|
||||
|
||||
func (migration *migrateMetaresourcesTuples) Up(ctx context.Context, db *bun.DB) error {
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
var storeID string
|
||||
err = tx.QueryRowContext(ctx, `SELECT id FROM store WHERE name = ? LIMIT 1`, "signoz").Scan(&storeID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Fetch all orgs.
|
||||
var orgIDs []string
|
||||
rows, err := tx.QueryContext(ctx, `SELECT id FROM organizations`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var orgID string
|
||||
if err := rows.Scan(&orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
orgIDs = append(orgIDs, orgID)
|
||||
}
|
||||
|
||||
isPG := migration.sqlstore.BunDB().Dialect().Name() == dialect.PG
|
||||
|
||||
// Step 1: Delete all tuples with the old "metaresources" object_type.
|
||||
for _, orgID := range orgIDs {
|
||||
if isPG {
|
||||
_, err = tx.ExecContext(ctx, `DELETE FROM tuple WHERE store = ? AND object_type = ? AND object_id LIKE ?`,
|
||||
storeID, "metaresources", "organization/"+orgID+"/%")
|
||||
} else {
|
||||
_, err = tx.ExecContext(ctx, `DELETE FROM tuple WHERE store = ? AND object_type = ? AND object_id LIKE ?`,
|
||||
storeID, "metaresources", "organization/"+orgID+"/%")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Also clean up changelog entries for the old type.
|
||||
if isPG {
|
||||
_, err = tx.ExecContext(ctx, `DELETE FROM changelog WHERE store = ? AND object_type = ?`,
|
||||
storeID, "metaresources")
|
||||
} else {
|
||||
_, err = tx.ExecContext(ctx, `DELETE FROM changelog WHERE store = ? AND object_type = ?`,
|
||||
storeID, "metaresources")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Insert replacement tuples.
|
||||
// For types with their own FGA type (user, serviceaccount, role), create/list
|
||||
// go on the type directly. For all other resources, create/list go on "metaresource".
|
||||
// Also add new detach tuples for role/user/serviceaccount.
|
||||
tuples := []migrationTuple{
|
||||
// New detach tuples for admin
|
||||
{authtypes.SigNozAdminRoleName, "role", "role", "detach"},
|
||||
{authtypes.SigNozAdminRoleName, "user", "user", "detach"},
|
||||
{authtypes.SigNozAdminRoleName, "serviceaccount", "serviceaccount", "detach"},
|
||||
// Replacement create/list for user/serviceaccount/role (moved from metaresources to own types)
|
||||
{authtypes.SigNozAdminRoleName, "serviceaccount", "serviceaccount", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "serviceaccount", "serviceaccount", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "user", "user", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "user", "user", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "role", "role", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "role", "role", "list"},
|
||||
// Replacement create/list for resources that move from "metaresources" to "metaresource"
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "auth-domain", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "auth-domain", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "cloud-integration", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "cloud-integration", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "cloud-integration-service", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "cloud-integration-service", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "integration", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "integration", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "factor-api-key", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "factor-api-key", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "factor-password", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "factor-password", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "license", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "license", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "subscription", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "subscription", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "organization", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "organization", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "org-preference", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "org-preference", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "public-dashboard", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "public-dashboard", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "session", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "dashboard", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "dashboard", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "pipeline", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "pipeline", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "planned-maintenance", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "planned-maintenance", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "rule", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "rule", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "saved-view", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "saved-view", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "trace-funnel", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "trace-funnel", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "ingestion-key", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "ingestion-key", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "ingestion-limit", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "ingestion-limit", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "notification-channel", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "notification-channel", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "route-policy", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "route-policy", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "apdex-setting", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "quick-filter", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "ttl-setting", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "user-preference", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "logs-field", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "traces-field", "list"},
|
||||
// Editor role: create/list replacements from metaresources → metaresource
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "dashboard", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "dashboard", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "pipeline", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "pipeline", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "planned-maintenance", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "planned-maintenance", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "rule", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "rule", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "saved-view", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "saved-view", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "trace-funnel", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "trace-funnel", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "integration", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "integration", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "ingestion-key", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "ingestion-key", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "ingestion-limit", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "ingestion-limit", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "notification-channel", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "route-policy", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "apdex-setting", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "quick-filter", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "ttl-setting", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "user-preference", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "logs-field", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "traces-field", "list"},
|
||||
// Viewer role: create/list replacements from metaresources → metaresource
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "dashboard", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "pipeline", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "planned-maintenance", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "rule", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "saved-view", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "trace-funnel", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "integration", "create"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "integration", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "notification-channel", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "route-policy", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "apdex-setting", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "quick-filter", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "ttl-setting", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "user-preference", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "logs-field", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "traces-field", "list"},
|
||||
}
|
||||
|
||||
for _, orgID := range orgIDs {
|
||||
for _, tuple := range tuples {
|
||||
entropy := ulid.DefaultEntropy()
|
||||
now := time.Now().UTC()
|
||||
tupleID := ulid.MustNew(ulid.Timestamp(now), entropy).String()
|
||||
|
||||
objectID := "organization/" + orgID + "/" + tuple.objectName + "/*"
|
||||
roleSubject := "organization/" + orgID + "/role/" + tuple.roleName
|
||||
|
||||
if isPG {
|
||||
user := "role:" + roleSubject + "#assignee"
|
||||
result, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO tuple (store, object_type, object_id, relation, _user, user_type, ulid, inserted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (store, object_type, object_id, relation, _user) DO NOTHING`,
|
||||
storeID, tuple.objectType, objectID, tuple.relation, user, "userset", tupleID, now,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
continue
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
INSERT INTO changelog (store, object_type, object_id, relation, _user, operation, ulid, inserted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (store, ulid, object_type) DO NOTHING`,
|
||||
storeID, tuple.objectType, objectID, tuple.relation, user, "TUPLE_OPERATION_WRITE", tupleID, now,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
result, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO tuple (store, object_type, object_id, relation, user_object_type, user_object_id, user_relation, user_type, ulid, inserted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (store, object_type, object_id, relation, user_object_type, user_object_id, user_relation) DO NOTHING`,
|
||||
storeID, tuple.objectType, objectID, tuple.relation, "role", roleSubject, "assignee", "userset", tupleID, now,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
continue
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
INSERT INTO changelog (store, object_type, object_id, relation, user_object_type, user_object_id, user_relation, operation, ulid, inserted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (store, ulid, object_type) DO NOTHING`,
|
||||
storeID, tuple.objectType, objectID, tuple.relation, "role", roleSubject, "assignee", 0, tupleID, now,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (migration *migrateMetaresourcesTuples) Down(context.Context, *bun.DB) error {
|
||||
return nil
|
||||
}
|
||||
@@ -18,46 +18,49 @@ const (
|
||||
|
||||
var ManagedRoleToTransactions = map[string][]Transaction{
|
||||
SigNozAdminRoleName: {
|
||||
// role attach — admin can attach/detach role assignments
|
||||
// role attach/detach — admin can attach/detach role assignments
|
||||
{Verb: VerbAttach, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
// user attach — admin can attach roles to any user
|
||||
{Verb: VerbDetach, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
// user attach/detach — admin can attach/detach roles to any user
|
||||
{Verb: VerbAttach, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
// serviceaccount attach — admin can attach roles to any SA
|
||||
{Verb: VerbDetach, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
// serviceaccount attach/detach — admin can attach/detach roles to any SA
|
||||
{Verb: VerbAttach, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbDetach, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
// auth-domain — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
// cloud-integration — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
// cloud-integration-service — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
// integration — viewer/editor/admin (install/uninstall via ViewAccess)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
// factor-api-key — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
// factor-password — admin can issue and inspect reset tokens; users change their own password via OpenAccess
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorPassword}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindFactorPassword}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindFactorPassword}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorPassword}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorPassword}, WildCardSelectorString)},
|
||||
// license — admin only.
|
||||
// Uniform LCRUD shape; actual ee routes are POST /api/v3/licenses (create
|
||||
// = Activate), PUT /api/v3/licenses (update = Refresh), GET
|
||||
@@ -67,8 +70,8 @@ var ManagedRoleToTransactions = map[string][]Transaction{
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLicense}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLicense}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLicense}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindLicense}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindLicense}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLicense}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLicense}, WildCardSelectorString)},
|
||||
// subscription — admin only.
|
||||
// Uniform LCRUD shape; actual ee routes are POST /api/v1/checkout
|
||||
// (create), POST /api/v1/portal (update — opens Stripe portal), GET
|
||||
@@ -78,123 +81,123 @@ var ManagedRoleToTransactions = map[string][]Transaction{
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
// organization — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeOrganization, Kind: KindOrganization}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeOrganization, Kind: KindOrganization}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindOrganization}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindOrganization}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrganization}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrganization}, WildCardSelectorString)},
|
||||
// org-preference — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
// public-dashboard — admin manages, anonymous reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
// role — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRole}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRole}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
// serviceaccount — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
// session — admin can revoke and list
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSession}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSession}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSession}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSession}, WildCardSelectorString)},
|
||||
// user — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindUser}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindUser}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
// dashboard — full CRUD (also held by editor)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
// pipeline — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
// planned-maintenance — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
// rule — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
// saved-view — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
// trace-funnel — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
// ingestion-key — editor+admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
// ingestion-limit — editor+admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
// notification-channel — admin writes, viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
// route-policy — admin writes, viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
// apdex-setting — admin updates, viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
// quick-filter — admin updates, viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
// ttl-setting — admin updates, viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
// user-preference — every authenticated user can read+update their own
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
// telemetry — read on each signal (logs/traces/metrics); schema permits read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindLogs}, WildCardSelectorString)},
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindTraces}, WildCardSelectorString)},
|
||||
@@ -207,86 +210,86 @@ var ManagedRoleToTransactions = map[string][]Transaction{
|
||||
// logs-field — editor+admin update (POST overwrites field config), viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
// traces-field — editor+admin update, viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
},
|
||||
SigNozEditorRoleName: {
|
||||
// dashboard — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
// pipeline — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
// planned-maintenance — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
// rule — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
// saved-view — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
// trace-funnel — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
// integration — viewer/editor/admin
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
// ingestion-key — editor+admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
// ingestion-limit — editor+admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
// notification-channel — read only (admin writes)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
// route-policy — read only (admin writes)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
// apdex-setting — read only (admin updates)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
// quick-filter — read only (admin updates)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
// ttl-setting — read only (admin updates)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
// user-preference — every authenticated user can read+update their own
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
// telemetry — read on each signal
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindLogs}, WildCardSelectorString)},
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindTraces}, WildCardSelectorString)},
|
||||
@@ -294,66 +297,66 @@ var ManagedRoleToTransactions = map[string][]Transaction{
|
||||
// logs-field — editor reads+updates
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
// traces-field — editor reads+updates
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
},
|
||||
SigNozViewerRoleName: {
|
||||
// dashboard — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
// pipeline — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
// planned-maintenance — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
// rule — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
// saved-view — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
// trace-funnel — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
// integration — viewer/editor/admin (install/uninstall via ViewAccess)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
// notification-channel — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
// route-policy — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
// apdex-setting — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
// quick-filter — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
// ttl-setting — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
// user-preference — every authenticated user can read+update their own
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
// telemetry — read on each signal
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindLogs}, WildCardSelectorString)},
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindTraces}, WildCardSelectorString)},
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindMetrics}, WildCardSelectorString)},
|
||||
// logs-field — viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
// traces-field — viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
},
|
||||
SigNozAnonymousRoleName: {
|
||||
// public-dashboard — anonymous read
|
||||
|
||||
@@ -6,138 +6,76 @@ var Resources = []Resource{
|
||||
ResourceRole,
|
||||
ResourceServiceAccount,
|
||||
ResourceUser,
|
||||
ResourceMetaResourcesRole,
|
||||
ResourceMetaResourcesOrganization,
|
||||
ResourceMetaResourcesServiceAccount,
|
||||
ResourceMetaResourcesUser,
|
||||
ResourceMetaResourceNotificationChannel,
|
||||
ResourceMetaResourcesNotificationChannel,
|
||||
ResourceMetaResourceRoutePolicy,
|
||||
ResourceMetaResourcesRoutePolicy,
|
||||
ResourceMetaResourceApdexSetting,
|
||||
ResourceMetaResourcesApdexSetting,
|
||||
ResourceMetaResourceAuthDomain,
|
||||
ResourceMetaResourcesAuthDomain,
|
||||
ResourceMetaResourceSession,
|
||||
ResourceMetaResourcesSession,
|
||||
ResourceMetaResourceCloudIntegration,
|
||||
ResourceMetaResourcesCloudIntegration,
|
||||
ResourceMetaResourceCloudIntegrationService,
|
||||
ResourceMetaResourcesCloudIntegrationService,
|
||||
ResourceMetaResourceIntegration,
|
||||
ResourceMetaResourcesIntegration,
|
||||
ResourceMetaResourceDashboard,
|
||||
ResourceMetaResourcesDashboard,
|
||||
ResourceMetaResourcePublicDashboard,
|
||||
ResourceMetaResourcesPublicDashboard,
|
||||
ResourceMetaResourceIngestionKey,
|
||||
ResourceMetaResourcesIngestionKey,
|
||||
ResourceMetaResourceIngestionLimit,
|
||||
ResourceMetaResourcesIngestionLimit,
|
||||
ResourceMetaResourcePipeline,
|
||||
ResourceMetaResourcesPipeline,
|
||||
ResourceMetaResourceUserPreference,
|
||||
ResourceMetaResourcesUserPreference,
|
||||
ResourceMetaResourceOrgPreference,
|
||||
ResourceMetaResourcesOrgPreference,
|
||||
ResourceMetaResourceQuickFilter,
|
||||
ResourceMetaResourcesQuickFilter,
|
||||
ResourceMetaResourceTTLSetting,
|
||||
ResourceMetaResourcesTTLSetting,
|
||||
ResourceMetaResourceRule,
|
||||
ResourceMetaResourcesRule,
|
||||
ResourceMetaResourcePlannedMaintenance,
|
||||
ResourceMetaResourcesPlannedMaintenance,
|
||||
ResourceMetaResourceSavedView,
|
||||
ResourceMetaResourcesSavedView,
|
||||
ResourceMetaResourceTraceFunnel,
|
||||
ResourceMetaResourcesTraceFunnel,
|
||||
ResourceMetaResourceFactorPassword,
|
||||
ResourceMetaResourcesFactorPassword,
|
||||
ResourceMetaResourceFactorAPIKey,
|
||||
ResourceMetaResourcesFactorAPIKey,
|
||||
ResourceMetaResourceLicense,
|
||||
ResourceMetaResourcesLicense,
|
||||
ResourceMetaResourceSubscription,
|
||||
ResourceMetaResourcesSubscription,
|
||||
ResourceTelemetryResourceLogs,
|
||||
ResourceTelemetryResourceTraces,
|
||||
ResourceTelemetryResourceMetrics,
|
||||
ResourceTelemetryResourceAuditLogs,
|
||||
ResourceTelemetryResourceMeterMetrics,
|
||||
ResourceMetaResourceLogsField,
|
||||
ResourceMetaResourcesLogsField,
|
||||
ResourceMetaResourceTracesField,
|
||||
ResourceMetaResourcesTracesField,
|
||||
}
|
||||
|
||||
var (
|
||||
ResourceAnonymous Resource = NewResourceAnonymous()
|
||||
ResourceOrganization = NewResourceOrganization()
|
||||
ResourceRole = NewResourceRole()
|
||||
ResourceServiceAccount = NewResourceServiceAccount()
|
||||
ResourceUser = NewResourceUser()
|
||||
ResourceMetaResourcesRole = NewResourceMetaResources(KindRole)
|
||||
ResourceMetaResourcesOrganization = NewResourceMetaResources(KindOrganization)
|
||||
ResourceMetaResourcesServiceAccount = NewResourceMetaResources(KindServiceAccount)
|
||||
ResourceMetaResourcesUser = NewResourceMetaResources(KindUser)
|
||||
ResourceMetaResourceNotificationChannel = NewResourceMetaResource(KindNotificationChannel)
|
||||
ResourceMetaResourcesNotificationChannel = NewResourceMetaResources(KindNotificationChannel)
|
||||
ResourceMetaResourceRoutePolicy = NewResourceMetaResource(KindRoutePolicy)
|
||||
ResourceMetaResourcesRoutePolicy = NewResourceMetaResources(KindRoutePolicy)
|
||||
ResourceMetaResourceApdexSetting = NewResourceMetaResource(KindApdexSetting)
|
||||
ResourceMetaResourcesApdexSetting = NewResourceMetaResources(KindApdexSetting)
|
||||
ResourceMetaResourceAuthDomain = NewResourceMetaResource(KindAuthDomain)
|
||||
ResourceMetaResourcesAuthDomain = NewResourceMetaResources(KindAuthDomain)
|
||||
ResourceMetaResourceSession = NewResourceMetaResource(KindSession)
|
||||
ResourceMetaResourcesSession = NewResourceMetaResources(KindSession)
|
||||
ResourceMetaResourceCloudIntegration = NewResourceMetaResource(KindCloudIntegration)
|
||||
ResourceMetaResourcesCloudIntegration = NewResourceMetaResources(KindCloudIntegration)
|
||||
ResourceMetaResourceCloudIntegrationService = NewResourceMetaResource(KindCloudIntegrationService)
|
||||
ResourceMetaResourcesCloudIntegrationService = NewResourceMetaResources(KindCloudIntegrationService)
|
||||
ResourceMetaResourceIntegration = NewResourceMetaResource(KindIntegration)
|
||||
ResourceMetaResourcesIntegration = NewResourceMetaResources(KindIntegration)
|
||||
ResourceMetaResourceDashboard = NewResourceMetaResource(KindDashboard)
|
||||
ResourceMetaResourcesDashboard = NewResourceMetaResources(KindDashboard)
|
||||
ResourceMetaResourcePublicDashboard = NewResourceMetaResource(KindPublicDashboard)
|
||||
ResourceMetaResourcesPublicDashboard = NewResourceMetaResources(KindPublicDashboard)
|
||||
ResourceMetaResourceIngestionKey = NewResourceMetaResource(KindIngestionKey)
|
||||
ResourceMetaResourcesIngestionKey = NewResourceMetaResources(KindIngestionKey)
|
||||
ResourceMetaResourceIngestionLimit = NewResourceMetaResource(KindIngestionLimit)
|
||||
ResourceMetaResourcesIngestionLimit = NewResourceMetaResources(KindIngestionLimit)
|
||||
ResourceMetaResourcePipeline = NewResourceMetaResource(KindPipeline)
|
||||
ResourceMetaResourcesPipeline = NewResourceMetaResources(KindPipeline)
|
||||
ResourceMetaResourceUserPreference = NewResourceMetaResource(KindUserPreference)
|
||||
ResourceMetaResourcesUserPreference = NewResourceMetaResources(KindUserPreference)
|
||||
ResourceMetaResourceOrgPreference = NewResourceMetaResource(KindOrgPreference)
|
||||
ResourceMetaResourcesOrgPreference = NewResourceMetaResources(KindOrgPreference)
|
||||
ResourceMetaResourceQuickFilter = NewResourceMetaResource(KindQuickFilter)
|
||||
ResourceMetaResourcesQuickFilter = NewResourceMetaResources(KindQuickFilter)
|
||||
ResourceMetaResourceTTLSetting = NewResourceMetaResource(KindTTLSetting)
|
||||
ResourceMetaResourcesTTLSetting = NewResourceMetaResources(KindTTLSetting)
|
||||
ResourceMetaResourceRule = NewResourceMetaResource(KindRule)
|
||||
ResourceMetaResourcesRule = NewResourceMetaResources(KindRule)
|
||||
ResourceMetaResourcePlannedMaintenance = NewResourceMetaResource(KindPlannedMaintenance)
|
||||
ResourceMetaResourcesPlannedMaintenance = NewResourceMetaResources(KindPlannedMaintenance)
|
||||
ResourceMetaResourceSavedView = NewResourceMetaResource(KindSavedView)
|
||||
ResourceMetaResourcesSavedView = NewResourceMetaResources(KindSavedView)
|
||||
ResourceMetaResourceTraceFunnel = NewResourceMetaResource(KindTraceFunnel)
|
||||
ResourceMetaResourcesTraceFunnel = NewResourceMetaResources(KindTraceFunnel)
|
||||
ResourceMetaResourceFactorPassword = NewResourceMetaResource(KindFactorPassword)
|
||||
ResourceMetaResourcesFactorPassword = NewResourceMetaResources(KindFactorPassword)
|
||||
ResourceMetaResourceFactorAPIKey = NewResourceMetaResource(KindFactorAPIKey)
|
||||
ResourceMetaResourcesFactorAPIKey = NewResourceMetaResources(KindFactorAPIKey)
|
||||
ResourceMetaResourceLicense = NewResourceMetaResource(KindLicense)
|
||||
ResourceMetaResourcesLicense = NewResourceMetaResources(KindLicense)
|
||||
ResourceMetaResourceSubscription = NewResourceMetaResource(KindSubscription)
|
||||
ResourceMetaResourcesSubscription = NewResourceMetaResources(KindSubscription)
|
||||
ResourceTelemetryResourceLogs = NewResourceTelemetryResource(KindLogs)
|
||||
ResourceTelemetryResourceTraces = NewResourceTelemetryResource(KindTraces)
|
||||
ResourceTelemetryResourceMetrics = NewResourceTelemetryResource(KindMetrics)
|
||||
ResourceTelemetryResourceAuditLogs = NewResourceTelemetryResource(KindAuditLogs)
|
||||
ResourceTelemetryResourceMeterMetrics = NewResourceTelemetryResource(KindMeterMetrics)
|
||||
ResourceMetaResourceLogsField = NewResourceMetaResource(KindLogsField)
|
||||
ResourceMetaResourcesLogsField = NewResourceMetaResources(KindLogsField)
|
||||
ResourceMetaResourceTracesField = NewResourceMetaResource(KindTracesField)
|
||||
ResourceMetaResourcesTracesField = NewResourceMetaResources(KindTracesField)
|
||||
ResourceAnonymous Resource = NewResourceAnonymous()
|
||||
ResourceOrganization = NewResourceOrganization()
|
||||
ResourceRole = NewResourceRole()
|
||||
ResourceServiceAccount = NewResourceServiceAccount()
|
||||
ResourceUser = NewResourceUser()
|
||||
ResourceMetaResourceNotificationChannel = NewResourceMetaResource(KindNotificationChannel)
|
||||
ResourceMetaResourceRoutePolicy = NewResourceMetaResource(KindRoutePolicy)
|
||||
ResourceMetaResourceApdexSetting = NewResourceMetaResource(KindApdexSetting)
|
||||
ResourceMetaResourceAuthDomain = NewResourceMetaResource(KindAuthDomain)
|
||||
ResourceMetaResourceSession = NewResourceMetaResource(KindSession)
|
||||
ResourceMetaResourceCloudIntegration = NewResourceMetaResource(KindCloudIntegration)
|
||||
ResourceMetaResourceCloudIntegrationService = NewResourceMetaResource(KindCloudIntegrationService)
|
||||
ResourceMetaResourceIntegration = NewResourceMetaResource(KindIntegration)
|
||||
ResourceMetaResourceDashboard = NewResourceMetaResource(KindDashboard)
|
||||
ResourceMetaResourcePublicDashboard = NewResourceMetaResource(KindPublicDashboard)
|
||||
ResourceMetaResourceIngestionKey = NewResourceMetaResource(KindIngestionKey)
|
||||
ResourceMetaResourceIngestionLimit = NewResourceMetaResource(KindIngestionLimit)
|
||||
ResourceMetaResourcePipeline = NewResourceMetaResource(KindPipeline)
|
||||
ResourceMetaResourceUserPreference = NewResourceMetaResource(KindUserPreference)
|
||||
ResourceMetaResourceOrgPreference = NewResourceMetaResource(KindOrgPreference)
|
||||
ResourceMetaResourceQuickFilter = NewResourceMetaResource(KindQuickFilter)
|
||||
ResourceMetaResourceTTLSetting = NewResourceMetaResource(KindTTLSetting)
|
||||
ResourceMetaResourceRule = NewResourceMetaResource(KindRule)
|
||||
ResourceMetaResourcePlannedMaintenance = NewResourceMetaResource(KindPlannedMaintenance)
|
||||
ResourceMetaResourceSavedView = NewResourceMetaResource(KindSavedView)
|
||||
ResourceMetaResourceTraceFunnel = NewResourceMetaResource(KindTraceFunnel)
|
||||
ResourceMetaResourceFactorPassword = NewResourceMetaResource(KindFactorPassword)
|
||||
ResourceMetaResourceFactorAPIKey = NewResourceMetaResource(KindFactorAPIKey)
|
||||
ResourceMetaResourceLicense = NewResourceMetaResource(KindLicense)
|
||||
ResourceMetaResourceSubscription = NewResourceMetaResource(KindSubscription)
|
||||
ResourceTelemetryResourceLogs = NewResourceTelemetryResource(KindLogs)
|
||||
ResourceTelemetryResourceTraces = NewResourceTelemetryResource(KindTraces)
|
||||
ResourceTelemetryResourceMetrics = NewResourceTelemetryResource(KindMetrics)
|
||||
ResourceTelemetryResourceAuditLogs = NewResourceTelemetryResource(KindAuditLogs)
|
||||
ResourceTelemetryResourceMeterMetrics = NewResourceTelemetryResource(KindMeterMetrics)
|
||||
ResourceMetaResourceLogsField = NewResourceMetaResource(KindLogsField)
|
||||
ResourceMetaResourceTracesField = NewResourceMetaResource(KindTracesField)
|
||||
)
|
||||
|
||||
@@ -13,17 +13,15 @@ var Types = []Type{
|
||||
TypeRole,
|
||||
TypeOrganization,
|
||||
TypeMetaResource,
|
||||
TypeMetaResources,
|
||||
TypeTelemetryResource,
|
||||
}
|
||||
|
||||
var (
|
||||
TypeUser = Type{valuer.NewString("user"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbAttach, VerbRead, VerbUpdate, VerbDelete}}
|
||||
TypeServiceAccount = Type{valuer.NewString("serviceaccount"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbAttach, VerbRead, VerbUpdate, VerbDelete}}
|
||||
TypeUser = Type{valuer.NewString("user"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbCreate, VerbList, VerbRead, VerbUpdate, VerbDelete, VerbAttach, VerbDetach}}
|
||||
TypeServiceAccount = Type{valuer.NewString("serviceaccount"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbCreate, VerbList, VerbRead, VerbUpdate, VerbDelete, VerbAttach, VerbDetach}}
|
||||
TypeAnonymous = Type{valuer.NewString("anonymous"), regexp.MustCompile(`^\*$`), []Verb{}}
|
||||
TypeRole = Type{valuer.NewString("role"), regexp.MustCompile(`^([a-z-]{1,50}|\*)$`), []Verb{VerbAssignee, VerbAttach, VerbRead, VerbUpdate, VerbDelete}}
|
||||
TypeRole = Type{valuer.NewString("role"), regexp.MustCompile(`^([a-z-]{1,50}|\*)$`), []Verb{VerbAssignee, VerbCreate, VerbList, VerbRead, VerbUpdate, VerbDelete, VerbAttach, VerbDetach}}
|
||||
TypeOrganization = Type{valuer.NewString("organization"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbRead, VerbUpdate, VerbDelete}}
|
||||
TypeMetaResource = Type{valuer.NewString("metaresource"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbRead, VerbUpdate, VerbDelete}}
|
||||
TypeMetaResources = Type{valuer.NewString("metaresources"), regexp.MustCompile(`^\*$`), []Verb{VerbCreate, VerbList}}
|
||||
TypeMetaResource = Type{valuer.NewString("metaresource"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbCreate, VerbList, VerbRead, VerbUpdate, VerbDelete, VerbAttach, VerbDetach}}
|
||||
TypeTelemetryResource = Type{valuer.NewString("telemetryresource"), regexp.MustCompile(`^\*$`), []Verb{VerbRead}}
|
||||
)
|
||||
|
||||
@@ -10,6 +10,7 @@ var Verbs = []Verb{
|
||||
VerbList,
|
||||
VerbAssignee,
|
||||
VerbAttach,
|
||||
VerbDetach,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -20,4 +21,5 @@ var (
|
||||
VerbList = Verb{valuer.NewString("list"), "listed"}
|
||||
VerbAssignee = Verb{valuer.NewString("assignee"), "assigned"}
|
||||
VerbAttach = Verb{valuer.NewString("attach"), "attached"}
|
||||
VerbDetach = Verb{valuer.NewString("detach"), "detached"}
|
||||
)
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package coretypes
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type resourceMetaResources struct {
|
||||
kind Kind
|
||||
}
|
||||
|
||||
func NewResourceMetaResources(kind Kind) Resource {
|
||||
return &resourceMetaResources{kind: kind}
|
||||
}
|
||||
|
||||
func (*resourceMetaResources) Type() Type {
|
||||
return TypeMetaResources
|
||||
}
|
||||
|
||||
func (resourceMetaResources *resourceMetaResources) Kind() Kind {
|
||||
return resourceMetaResources.kind
|
||||
}
|
||||
|
||||
// example: metaresources:organization/0199c47d-f61b-7833-bc5f-c0730f12f046/dashboards
|
||||
func (resourceMetaResources *resourceMetaResources) Prefix(orgID valuer.UUID) string {
|
||||
return resourceMetaResources.Type().StringValue() + ":" + "organization" + "/" + orgID.StringValue() + "/" + resourceMetaResources.Kind().String()
|
||||
}
|
||||
|
||||
func (resourceMetaResources *resourceMetaResources) Object(orgID valuer.UUID, selector string) string {
|
||||
return resourceMetaResources.Prefix(orgID) + "/" + selector
|
||||
}
|
||||
|
||||
func (resourceMetaResources *resourceMetaResources) Scope(verb Verb) string {
|
||||
return resourceMetaResources.Kind().String() + ":" + verb.StringValue()
|
||||
}
|
||||
@@ -33,8 +33,6 @@ func NewType(input string) (Type, error) {
|
||||
return TypeOrganization, nil
|
||||
case "metaresource":
|
||||
return TypeMetaResource, nil
|
||||
case "metaresources":
|
||||
return TypeMetaResources, nil
|
||||
default:
|
||||
return Type{}, errors.Newf(errors.TypeInvalidInput, ErrCodeInvalidType, "invalid type: %s", input)
|
||||
}
|
||||
@@ -80,7 +78,6 @@ func (typed Type) Enum() []any {
|
||||
TypeRole,
|
||||
TypeOrganization,
|
||||
TypeMetaResource,
|
||||
TypeMetaResources,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ func NewVerb(verb string) (Verb, error) {
|
||||
return VerbAssignee, nil
|
||||
case "attach":
|
||||
return VerbAttach, nil
|
||||
case "detach":
|
||||
return VerbDetach, nil
|
||||
default:
|
||||
return Verb{}, errors.Newf(errors.TypeInvalidInput, ErrCodeInvalidVerb, "verb %s is invalid, valid verbs are: %s", verb, Verb{}.Enum())
|
||||
}
|
||||
@@ -44,6 +46,7 @@ func (Verb) Enum() []any {
|
||||
VerbList,
|
||||
VerbAssignee,
|
||||
VerbAttach,
|
||||
VerbDetach,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
326
tests/integration/tests/role/03_fga.py
Normal file
326
tests/integration/tests/role/03_fga.py
Normal file
@@ -0,0 +1,326 @@
|
||||
"""Tests for resource-level FGA on role endpoints.
|
||||
|
||||
Validates that a custom role with specific role permissions gets exactly
|
||||
the access it was granted — read/list allowed, create/update/delete forbidden
|
||||
until explicitly granted, and revocation removes access.
|
||||
"""
|
||||
|
||||
from collections.abc import Callable
|
||||
from http import HTTPStatus
|
||||
|
||||
import requests
|
||||
from wiremock.resources.mappings import Mapping
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.auth import (
|
||||
USER_ADMIN_EMAIL,
|
||||
USER_ADMIN_PASSWORD,
|
||||
add_license,
|
||||
change_user_role,
|
||||
create_active_user,
|
||||
find_user_by_email,
|
||||
)
|
||||
from fixtures.role import (
|
||||
ROLES_BASE,
|
||||
create_custom_role,
|
||||
delete_custom_role,
|
||||
find_role_by_name,
|
||||
object_group,
|
||||
patch_role_objects,
|
||||
)
|
||||
|
||||
ROLE_FGA_CUSTOM_ROLE_NAME = "role-fga-readonly"
|
||||
ROLE_FGA_CUSTOM_USER_EMAIL = "customrole+rolefga@integration.test"
|
||||
ROLE_FGA_CUSTOM_USER_PASSWORD = "password123Z$"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 1. Apply license (required for custom role CRUD)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_apply_license(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
make_http_mocks: Callable[[types.TestContainerDocker, list[Mapping]], None],
|
||||
get_token: Callable[[str, str], str],
|
||||
) -> None:
|
||||
add_license(signoz, make_http_mocks, get_token)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. Create custom role + user with read/list on roles
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_create_custom_role_for_role_fga(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
# Create the custom role.
|
||||
role_id = create_custom_role(signoz, admin_token, ROLE_FGA_CUSTOM_ROLE_NAME)
|
||||
|
||||
# Grant read on role instances.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"read",
|
||||
additions=[
|
||||
object_group("role", "role", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
# Grant list on role collection.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"list",
|
||||
additions=[
|
||||
object_group("role", "role", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
# Create the custom-role user: invite as VIEWER, activate, change role.
|
||||
user_id = create_active_user(
|
||||
signoz,
|
||||
admin_token,
|
||||
email=ROLE_FGA_CUSTOM_USER_EMAIL,
|
||||
role="VIEWER",
|
||||
password=ROLE_FGA_CUSTOM_USER_PASSWORD,
|
||||
name="role-fga-test-user",
|
||||
)
|
||||
change_user_role(signoz, admin_token, user_id, "signoz-viewer", ROLE_FGA_CUSTOM_ROLE_NAME)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 3. Read-only access: allowed operations
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_role_readonly_allowed_operations(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
token = get_token(ROLE_FGA_CUSTOM_USER_EMAIL, ROLE_FGA_CUSTOM_USER_PASSWORD)
|
||||
target_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# List roles.
|
||||
resp = requests.get(
|
||||
signoz.self.host_configs["8080"].get(ROLES_BASE),
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.OK, f"list roles: {resp.text}"
|
||||
|
||||
# Get role.
|
||||
resp = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{target_role_id}"),
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.OK, f"get role: {resp.text}"
|
||||
|
||||
# Get objects for role.
|
||||
resp = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{target_role_id}/relations/read/objects"),
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.OK, f"get role objects: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 4. Read-only access: forbidden operations
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_role_readonly_forbidden_operations(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
token = get_token(ROLE_FGA_CUSTOM_USER_EMAIL, ROLE_FGA_CUSTOM_USER_PASSWORD)
|
||||
target_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# Create role — forbidden.
|
||||
resp = requests.post(
|
||||
signoz.self.host_configs["8080"].get(ROLES_BASE),
|
||||
json={"name": "role-fga-should-fail"},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"create role: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Patch role — forbidden.
|
||||
resp = requests.patch(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{target_role_id}"),
|
||||
json={"name": "role-fga-renamed"},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"patch role: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Patch objects — forbidden.
|
||||
resp = requests.patch(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{target_role_id}/relations/read/objects"),
|
||||
json={"additions": [object_group("metaresource", "dashboard", ["*"])]},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"patch objects: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Delete role — forbidden (cannot delete managed role, but auth check comes first).
|
||||
# Use the custom role itself as target (non-managed, but user lacks delete permission).
|
||||
custom_role_id = find_role_by_name(signoz, admin_token, ROLE_FGA_CUSTOM_ROLE_NAME)
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{custom_role_id}"),
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"delete role: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 5. Grant write permissions, verify access opens up
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_role_grant_write_permissions(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
role_id = find_role_by_name(signoz, admin_token, ROLE_FGA_CUSTOM_ROLE_NAME)
|
||||
|
||||
# Grant create, update, delete on roles.
|
||||
for verb in ("create", "update", "delete"):
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
verb,
|
||||
additions=[object_group("role", "role", ["*"])],
|
||||
)
|
||||
|
||||
custom_token = get_token(ROLE_FGA_CUSTOM_USER_EMAIL, ROLE_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
# Create role — now allowed.
|
||||
resp = requests.post(
|
||||
signoz.self.host_configs["8080"].get(ROLES_BASE),
|
||||
json={"name": "role-fga-write-test"},
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.CREATED, f"create role: {resp.text}"
|
||||
new_role_id = resp.json()["data"]["id"]
|
||||
|
||||
# Patch role — now allowed.
|
||||
resp = requests.patch(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{new_role_id}"),
|
||||
json={"name": "role-fga-write-renamed"},
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"patch role: {resp.text}"
|
||||
|
||||
# Delete role — now allowed.
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{new_role_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"delete role: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 6. Revoke read/list → verify access lost
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_role_revoke_read_permissions(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
role_id = find_role_by_name(signoz, admin_token, ROLE_FGA_CUSTOM_ROLE_NAME)
|
||||
target_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# Revoke read.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"read",
|
||||
deletions=[object_group("role", "role", ["*"])],
|
||||
)
|
||||
|
||||
# Revoke list.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"list",
|
||||
deletions=[object_group("role", "role", ["*"])],
|
||||
)
|
||||
|
||||
custom_token = get_token(ROLE_FGA_CUSTOM_USER_EMAIL, ROLE_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
# List roles — forbidden.
|
||||
resp = requests.get(
|
||||
signoz.self.host_configs["8080"].get(ROLES_BASE),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"list roles after revoke: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Get role — forbidden.
|
||||
resp = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{target_role_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"get role after revoke: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 7. Clean up: delete custom role
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_role_fga_cleanup(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
role_id = find_role_by_name(signoz, admin_token, ROLE_FGA_CUSTOM_ROLE_NAME)
|
||||
user = find_user_by_email(signoz, admin_token, ROLE_FGA_CUSTOM_USER_EMAIL)
|
||||
|
||||
# Remove the custom role from the user first.
|
||||
resp = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"/api/v2/users/{user['id']}/roles"),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.OK, resp.text
|
||||
roles = resp.json()["data"]
|
||||
custom_entry = next((r for r in roles if r["name"] == ROLE_FGA_CUSTOM_ROLE_NAME), None)
|
||||
if custom_entry is not None:
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"/api/v2/users/{user['id']}/roles/{custom_entry['id']}"),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"remove role from user: {resp.text}"
|
||||
|
||||
delete_custom_role(signoz, admin_token, role_id)
|
||||
@@ -1,8 +1,11 @@
|
||||
"""Tests for resource-level FGA on service account endpoints.
|
||||
|
||||
Validates that a custom role with specific SA permissions gets exactly
|
||||
the access it was granted, and that SA role assignment requires BOTH
|
||||
serviceaccount:attach AND role:attach.
|
||||
the access it was granted, and that:
|
||||
- SA role assignment requires BOTH serviceaccount:attach AND role:attach.
|
||||
- SA role removal requires BOTH serviceaccount:detach AND role:detach.
|
||||
- Factor API key creation requires factor-api-key:create AND serviceaccount:attach.
|
||||
- Factor API key revocation requires factor-api-key:delete AND serviceaccount:detach.
|
||||
"""
|
||||
|
||||
from collections.abc import Callable
|
||||
@@ -80,14 +83,36 @@ def test_create_custom_role_readonly_sa(
|
||||
],
|
||||
)
|
||||
|
||||
# Grant list on serviceaccount collection.
|
||||
# Grant list on serviceaccount (now on the serviceaccount type directly).
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"list",
|
||||
additions=[
|
||||
object_group("metaresources", "serviceaccount", ["*"]),
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
# Grant read on factor-api-key (needed for listing keys).
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"read",
|
||||
additions=[
|
||||
object_group("metaresource", "factor-api-key", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
# Grant list on factor-api-key.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"list",
|
||||
additions=[
|
||||
object_group("metaresource", "factor-api-key", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -203,7 +228,7 @@ def test_readonly_role_forbidden_operations(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"delete SA: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Assign role to SA — forbidden.
|
||||
# Assign role to SA — forbidden (needs attach on both SA and role).
|
||||
resp = requests.post(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles"),
|
||||
json={"id": viewer_role_id},
|
||||
@@ -212,7 +237,7 @@ def test_readonly_role_forbidden_operations(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"assign SA role: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Remove role from SA — forbidden.
|
||||
# Remove role from SA — forbidden (needs detach on both SA and role).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles/{viewer_role_id}"),
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
@@ -220,7 +245,7 @@ def test_readonly_role_forbidden_operations(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"remove SA role: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Create key — forbidden (needs update).
|
||||
# Create key — forbidden (needs factor-api-key:create + serviceaccount:attach).
|
||||
resp = requests.post(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/keys"),
|
||||
json={"name": "fga-key-fail", "expiresAt": 0},
|
||||
@@ -229,7 +254,7 @@ def test_readonly_role_forbidden_operations(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"create key: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Revoke key — forbidden (needs update).
|
||||
# Revoke key — forbidden (needs factor-api-key:delete + serviceaccount:detach).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/keys/{key_id}"),
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
@@ -253,14 +278,14 @@ def test_patch_role_add_write_permissions(
|
||||
sa_id = find_service_account_by_name(signoz, admin_token, SA_FGA_TARGET_SA_NAME)["id"]
|
||||
viewer_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# Grant create on collection.
|
||||
# Grant create on serviceaccount (now on serviceaccount type directly).
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"create",
|
||||
additions=[
|
||||
object_group("metaresources", "serviceaccount", ["*"]),
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -286,6 +311,44 @@ def test_patch_role_add_write_permissions(
|
||||
],
|
||||
)
|
||||
|
||||
# Grant factor-api-key create/delete + serviceaccount attach/detach for key operations.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"create",
|
||||
additions=[
|
||||
object_group("metaresource", "factor-api-key", ["*"]),
|
||||
],
|
||||
)
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"delete",
|
||||
additions=[
|
||||
object_group("metaresource", "factor-api-key", ["*"]),
|
||||
],
|
||||
)
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"attach",
|
||||
additions=[
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"detach",
|
||||
additions=[
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
custom_token = get_token(SA_FGA_CUSTOM_USER_EMAIL, SA_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
# Create SA — now allowed.
|
||||
@@ -307,7 +370,7 @@ def test_patch_role_add_write_permissions(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"update SA: {resp.text}"
|
||||
|
||||
# Create key — now allowed (update permission covers key create).
|
||||
# Create key — now allowed (factor-api-key:create + serviceaccount:attach).
|
||||
key_resp = requests.post(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{new_sa_id}/keys"),
|
||||
json={"name": "fga-write-key", "expiresAt": 0},
|
||||
@@ -317,7 +380,7 @@ def test_patch_role_add_write_permissions(
|
||||
assert key_resp.status_code == HTTPStatus.CREATED, f"create key: {key_resp.text}"
|
||||
new_key_id = key_resp.json()["data"]["id"]
|
||||
|
||||
# Revoke key — now allowed (update permission covers key revoke).
|
||||
# Revoke key — now allowed (factor-api-key:delete + serviceaccount:detach).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{new_sa_id}/keys/{new_key_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
@@ -333,7 +396,7 @@ def test_patch_role_add_write_permissions(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"delete SA: {resp.text}"
|
||||
|
||||
# Role assignment still forbidden (no attach).
|
||||
# Role assignment still forbidden (has attach on SA but not on role).
|
||||
resp = requests.post(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles"),
|
||||
json={"id": viewer_role_id},
|
||||
@@ -342,6 +405,7 @@ def test_patch_role_add_write_permissions(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"assign SA role: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Role removal still forbidden (has detach on SA but not on role).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles/{viewer_role_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
@@ -351,7 +415,7 @@ def test_patch_role_add_write_permissions(
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 6. Dual-attach: SA attach only (no role attach) → forbidden
|
||||
# 6. Dual-attach: SA attach only (no role attach) → assign forbidden
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -361,21 +425,10 @@ def test_attach_with_only_sa_attach_forbidden(
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
role_id = find_role_by_name(signoz, admin_token, SA_FGA_CUSTOM_ROLE_NAME)
|
||||
sa_id = find_service_account_by_name(signoz, admin_token, SA_FGA_TARGET_SA_NAME)["id"]
|
||||
viewer_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# Grant attach on serviceaccount only.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"attach",
|
||||
additions=[
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
# SA attach already granted from previous test; role attach not yet granted.
|
||||
custom_token = get_token(SA_FGA_CUSTOM_USER_EMAIL, SA_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
# Assign role — forbidden (has SA attach, missing role attach).
|
||||
@@ -387,17 +440,36 @@ def test_attach_with_only_sa_attach_forbidden(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"assign with only SA attach: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Remove role — forbidden (CheckAll: role attach group fails).
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 7. Dual-detach: SA detach only (no role detach) → remove forbidden
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_detach_with_only_sa_detach_forbidden(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
sa_id = find_service_account_by_name(signoz, admin_token, SA_FGA_TARGET_SA_NAME)["id"]
|
||||
viewer_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# SA detach already granted from test_patch_role_add_write_permissions;
|
||||
# role detach not yet granted.
|
||||
custom_token = get_token(SA_FGA_CUSTOM_USER_EMAIL, SA_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
# Remove role — forbidden (has SA detach, missing role detach).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles/{viewer_role_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"remove with only SA attach: expected 403, got {resp.status_code}: {resp.text}"
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"remove with only SA detach: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 7. Dual-attach: role attach only (no SA attach) → forbidden
|
||||
# 8. Dual-attach: role attach only (no SA attach) → assign forbidden
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -432,21 +504,49 @@ def test_attach_with_only_role_attach_forbidden(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"assign with only role attach: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Remove role — forbidden (CheckAll: SA attach group fails).
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 9. Dual-detach: role detach only (no SA detach) → remove forbidden
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_detach_with_only_role_detach_forbidden(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
role_id = find_role_by_name(signoz, admin_token, SA_FGA_CUSTOM_ROLE_NAME)
|
||||
sa_id = find_service_account_by_name(signoz, admin_token, SA_FGA_TARGET_SA_NAME)["id"]
|
||||
viewer_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# Remove SA detach, grant role detach.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"detach",
|
||||
additions=[object_group("role", "role", ["*"])],
|
||||
deletions=[object_group("serviceaccount", "serviceaccount", ["*"])],
|
||||
)
|
||||
|
||||
custom_token = get_token(SA_FGA_CUSTOM_USER_EMAIL, SA_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
# Remove role — forbidden (SA detach check fails).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles/{viewer_role_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"remove with only role attach: expected 403, got {resp.status_code}: {resp.text}"
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"remove with only role detach: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 8. Dual-attach: both SA + role attach → succeeds
|
||||
# 10. Both attach + detach → assign and remove succeed
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_attach_with_both_permissions_succeeds(
|
||||
def test_attach_detach_with_both_permissions_succeeds(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
@@ -455,7 +555,7 @@ def test_attach_with_both_permissions_succeeds(
|
||||
role_id = find_role_by_name(signoz, admin_token, SA_FGA_CUSTOM_ROLE_NAME)
|
||||
sa_id = find_service_account_by_name(signoz, admin_token, SA_FGA_TARGET_SA_NAME)["id"]
|
||||
|
||||
# Add back SA attach (role attach already present from previous test).
|
||||
# Add back SA attach and SA detach (role attach/detach already present from previous tests).
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
@@ -465,6 +565,15 @@ def test_attach_with_both_permissions_succeeds(
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"detach",
|
||||
additions=[
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
custom_token = get_token(SA_FGA_CUSTOM_USER_EMAIL, SA_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
@@ -480,17 +589,17 @@ def test_attach_with_both_permissions_succeeds(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"assign with both attach: {resp.text}"
|
||||
|
||||
# Remove the editor role — should succeed (CheckAll: both groups pass).
|
||||
# Remove the editor role — should succeed (both SA detach + role detach).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles/{editor_role_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"remove with both attach: {resp.text}"
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"remove with both detach: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 9. Revoke read/list → verify access lost
|
||||
# 11. Revoke read/list → verify access lost
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -514,14 +623,14 @@ def test_remove_read_permissions_revokes_access(
|
||||
],
|
||||
)
|
||||
|
||||
# Revoke list.
|
||||
# Revoke list (now on serviceaccount type directly).
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"list",
|
||||
deletions=[
|
||||
object_group("metaresources", "serviceaccount", ["*"]),
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -545,7 +654,7 @@ def test_remove_read_permissions_revokes_access(
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 10. Clean up: delete custom role
|
||||
# 12. Clean up: delete custom role
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user