mirror of
https://github.com/SigNoz/signoz.git
synced 2026-02-03 08:33:26 +00:00
feat(authz): scafolding for rbac migration (#10121)
* feat(authz): initial commit for migrating rbac to openfga * feat(authz): make the role updates idempotant * feat(authz): split role module into role and grant * feat(authz): some naming changes * feat(authz): integrate the grant module * feat(authz): add support for migrating existing user role * feat(authz): add support for migrating existing user role * feat(authz): figure out the * selector * feat(authz): merge main * feat(authz): merge main * feat(authz): address couple of todos * feat(authz): address couple of todos * feat(authz): fix tests and revert public dashboard change * feat(authz): fix tests and revert public dashboard change * feat(authz): add open api spec * feat(authz): add open api spec * feat(authz): add api key changes and missing migration * feat(authz): split role into getter and setter * feat(authz): add integration tests for authz register * feat(authz): add more tests for user invite and delete * feat(authz): update user tests * feat(authz): rename grant to granter * feat(authz): address review comments * feat(authz): address review comments * feat(authz): address review comments * feat(authz): add the migration for existing roles * feat(authz): go mod tidy * feat(authz): fix integration tests * feat(authz): handle community changes * feat(authz): handle community changes * feat(authz): role selectors for open claims * feat(authz): role selectors for open claims * feat(authz): prevent duplicate entries for changelog * feat(authz): scafolding for rbac migration * feat(authz): scafolding for rbac migration * feat(authz): scafolding for rbac migration * feat(authz): scafolding for rbac migration * feat(authz): scafolding for rbac migration
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/role"
|
"github.com/SigNoz/signoz/pkg/modules/role"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role/implrole"
|
||||||
"github.com/SigNoz/signoz/pkg/querier"
|
"github.com/SigNoz/signoz/pkg/querier"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app"
|
"github.com/SigNoz/signoz/pkg/query-service/app"
|
||||||
"github.com/SigNoz/signoz/pkg/queryparser"
|
"github.com/SigNoz/signoz/pkg/queryparser"
|
||||||
@@ -80,12 +81,15 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
|||||||
func(ctx context.Context, sqlstore sqlstore.SQLStore) factory.ProviderFactory[authz.AuthZ, authz.Config] {
|
func(ctx context.Context, sqlstore sqlstore.SQLStore) factory.ProviderFactory[authz.AuthZ, authz.Config] {
|
||||||
return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx))
|
return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx))
|
||||||
},
|
},
|
||||||
func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, _ role.Module, queryParser queryparser.QueryParser, _ querier.Querier, _ licensing.Licensing) dashboard.Module {
|
func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, _ role.Setter, _ role.Granter, queryParser queryparser.QueryParser, _ querier.Querier, _ licensing.Licensing) dashboard.Module {
|
||||||
return impldashboard.NewModule(impldashboard.NewStore(store), settings, analytics, orgGetter, queryParser)
|
return impldashboard.NewModule(impldashboard.NewStore(store), settings, analytics, orgGetter, queryParser)
|
||||||
},
|
},
|
||||||
func(_ licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config] {
|
func(_ licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config] {
|
||||||
return noopgateway.NewProviderFactory()
|
return noopgateway.NewProviderFactory()
|
||||||
},
|
},
|
||||||
|
func(store sqlstore.SQLStore, authz authz.AuthZ, licensing licensing.Licensing, _ []role.RegisterTypeable) role.Setter {
|
||||||
|
return implrole.NewSetter(implrole.NewStore(store), authz)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorContext(ctx, "failed to create signoz", "error", err)
|
logger.ErrorContext(ctx, "failed to create signoz", "error", err)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
enterpriselicensing "github.com/SigNoz/signoz/ee/licensing"
|
enterpriselicensing "github.com/SigNoz/signoz/ee/licensing"
|
||||||
"github.com/SigNoz/signoz/ee/licensing/httplicensing"
|
"github.com/SigNoz/signoz/ee/licensing/httplicensing"
|
||||||
"github.com/SigNoz/signoz/ee/modules/dashboard/impldashboard"
|
"github.com/SigNoz/signoz/ee/modules/dashboard/impldashboard"
|
||||||
|
"github.com/SigNoz/signoz/ee/modules/role/implrole"
|
||||||
enterpriseapp "github.com/SigNoz/signoz/ee/query-service/app"
|
enterpriseapp "github.com/SigNoz/signoz/ee/query-service/app"
|
||||||
"github.com/SigNoz/signoz/ee/sqlschema/postgressqlschema"
|
"github.com/SigNoz/signoz/ee/sqlschema/postgressqlschema"
|
||||||
"github.com/SigNoz/signoz/ee/sqlstore/postgressqlstore"
|
"github.com/SigNoz/signoz/ee/sqlstore/postgressqlstore"
|
||||||
@@ -29,6 +30,7 @@ import (
|
|||||||
pkgimpldashboard "github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
pkgimpldashboard "github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/role"
|
"github.com/SigNoz/signoz/pkg/modules/role"
|
||||||
|
pkgimplrole "github.com/SigNoz/signoz/pkg/modules/role/implrole"
|
||||||
"github.com/SigNoz/signoz/pkg/querier"
|
"github.com/SigNoz/signoz/pkg/querier"
|
||||||
"github.com/SigNoz/signoz/pkg/queryparser"
|
"github.com/SigNoz/signoz/pkg/queryparser"
|
||||||
"github.com/SigNoz/signoz/pkg/signoz"
|
"github.com/SigNoz/signoz/pkg/signoz"
|
||||||
@@ -119,13 +121,17 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
|||||||
func(ctx context.Context, sqlstore sqlstore.SQLStore) factory.ProviderFactory[authz.AuthZ, authz.Config] {
|
func(ctx context.Context, sqlstore sqlstore.SQLStore) factory.ProviderFactory[authz.AuthZ, authz.Config] {
|
||||||
return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx))
|
return openfgaauthz.NewProviderFactory(sqlstore, openfgaschema.NewSchema().Get(ctx))
|
||||||
},
|
},
|
||||||
func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, role role.Module, queryParser queryparser.QueryParser, querier querier.Querier, licensing licensing.Licensing) dashboard.Module {
|
func(store sqlstore.SQLStore, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, roleSetter role.Setter, granter role.Granter, queryParser queryparser.QueryParser, querier querier.Querier, licensing licensing.Licensing) dashboard.Module {
|
||||||
return impldashboard.NewModule(pkgimpldashboard.NewStore(store), settings, analytics, orgGetter, role, queryParser, querier, licensing)
|
return impldashboard.NewModule(pkgimpldashboard.NewStore(store), settings, analytics, orgGetter, roleSetter, granter, queryParser, querier, licensing)
|
||||||
},
|
},
|
||||||
func(licensing licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config] {
|
func(licensing licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config] {
|
||||||
return httpgateway.NewProviderFactory(licensing)
|
return httpgateway.NewProviderFactory(licensing)
|
||||||
},
|
},
|
||||||
|
func(store sqlstore.SQLStore, authz authz.AuthZ, licensing licensing.Licensing, registry []role.RegisterTypeable) role.Setter {
|
||||||
|
return implrole.NewSetter(pkgimplrole.NewStore(store), authz, licensing, registry)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorContext(ctx, "failed to create signoz", "error", err)
|
logger.ErrorContext(ctx, "failed to create signoz", "error", err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ paths:
|
|||||||
/api/v1/dashboards/{id}/public:
|
/api/v1/dashboards/{id}/public:
|
||||||
delete:
|
delete:
|
||||||
deprecated: false
|
deprecated: false
|
||||||
description: This endpoints deletes the public sharing config and disables the
|
description: This endpoint deletes the public sharing config and disables the
|
||||||
public sharing of a dashboard
|
public sharing of a dashboard
|
||||||
operationId: DeletePublicDashboard
|
operationId: DeletePublicDashboard
|
||||||
parameters:
|
parameters:
|
||||||
@@ -253,7 +253,7 @@ paths:
|
|||||||
- dashboard
|
- dashboard
|
||||||
get:
|
get:
|
||||||
deprecated: false
|
deprecated: false
|
||||||
description: This endpoints returns public sharing config for a dashboard
|
description: This endpoint returns public sharing config for a dashboard
|
||||||
operationId: GetPublicDashboard
|
operationId: GetPublicDashboard
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
@@ -301,7 +301,7 @@ paths:
|
|||||||
- dashboard
|
- dashboard
|
||||||
post:
|
post:
|
||||||
deprecated: false
|
deprecated: false
|
||||||
description: This endpoints creates public sharing config and enables public
|
description: This endpoint creates public sharing config and enables public
|
||||||
sharing of the dashboard
|
sharing of the dashboard
|
||||||
operationId: CreatePublicDashboard
|
operationId: CreatePublicDashboard
|
||||||
parameters:
|
parameters:
|
||||||
@@ -355,7 +355,7 @@ paths:
|
|||||||
- dashboard
|
- dashboard
|
||||||
put:
|
put:
|
||||||
deprecated: false
|
deprecated: false
|
||||||
description: This endpoints updates the public sharing config for a dashboard
|
description: This endpoint updates the public sharing config for a dashboard
|
||||||
operationId: UpdatePublicDashboard
|
operationId: UpdatePublicDashboard
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
@@ -671,7 +671,7 @@ paths:
|
|||||||
/api/v1/global/config:
|
/api/v1/global/config:
|
||||||
get:
|
get:
|
||||||
deprecated: false
|
deprecated: false
|
||||||
description: This endpoints returns global config
|
description: This endpoint returns global config
|
||||||
operationId: GetGlobalConfig
|
operationId: GetGlobalConfig
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
@@ -1447,8 +1447,7 @@ paths:
|
|||||||
/api/v1/public/dashboards/{id}:
|
/api/v1/public/dashboards/{id}:
|
||||||
get:
|
get:
|
||||||
deprecated: false
|
deprecated: false
|
||||||
description: This endpoints returns the sanitized dashboard data for public
|
description: This endpoint returns the sanitized dashboard data for public access
|
||||||
access
|
|
||||||
operationId: GetPublicDashboardData
|
operationId: GetPublicDashboardData
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
@@ -1579,6 +1578,228 @@ paths:
|
|||||||
summary: Reset password
|
summary: Reset password
|
||||||
tags:
|
tags:
|
||||||
- users
|
- users
|
||||||
|
/api/v1/roles:
|
||||||
|
get:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint lists all roles
|
||||||
|
operationId: ListRoles
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/RoletypesRole'
|
||||||
|
type: array
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
description: OK
|
||||||
|
"401":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Unauthorized
|
||||||
|
"403":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Forbidden
|
||||||
|
"500":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Internal Server Error
|
||||||
|
security:
|
||||||
|
- api_key:
|
||||||
|
- ADMIN
|
||||||
|
- tokenizer:
|
||||||
|
- ADMIN
|
||||||
|
summary: List roles
|
||||||
|
tags:
|
||||||
|
- role
|
||||||
|
post:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint creates a role
|
||||||
|
operationId: CreateRole
|
||||||
|
responses:
|
||||||
|
"201":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/components/schemas/TypesIdentifiable'
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
description: Created
|
||||||
|
"401":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Unauthorized
|
||||||
|
"403":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Forbidden
|
||||||
|
"500":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Internal Server Error
|
||||||
|
security:
|
||||||
|
- api_key:
|
||||||
|
- ADMIN
|
||||||
|
- tokenizer:
|
||||||
|
- ADMIN
|
||||||
|
summary: Create role
|
||||||
|
tags:
|
||||||
|
- role
|
||||||
|
/api/v1/roles/{id}:
|
||||||
|
delete:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint deletes a role
|
||||||
|
operationId: DeleteRole
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: No Content
|
||||||
|
"401":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Unauthorized
|
||||||
|
"403":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Forbidden
|
||||||
|
"500":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Internal Server Error
|
||||||
|
security:
|
||||||
|
- api_key:
|
||||||
|
- ADMIN
|
||||||
|
- tokenizer:
|
||||||
|
- ADMIN
|
||||||
|
summary: Delete role
|
||||||
|
tags:
|
||||||
|
- role
|
||||||
|
get:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint gets a role
|
||||||
|
operationId: GetRole
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/components/schemas/RoletypesRole'
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
description: OK
|
||||||
|
"401":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Unauthorized
|
||||||
|
"403":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Forbidden
|
||||||
|
"500":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Internal Server Error
|
||||||
|
security:
|
||||||
|
- api_key:
|
||||||
|
- ADMIN
|
||||||
|
- tokenizer:
|
||||||
|
- ADMIN
|
||||||
|
summary: Get role
|
||||||
|
tags:
|
||||||
|
- role
|
||||||
|
patch:
|
||||||
|
deprecated: false
|
||||||
|
description: This endpoint patches a role
|
||||||
|
operationId: PatchRole
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: No Content
|
||||||
|
"401":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Unauthorized
|
||||||
|
"403":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Forbidden
|
||||||
|
"500":
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
|
description: Internal Server Error
|
||||||
|
security:
|
||||||
|
- api_key:
|
||||||
|
- ADMIN
|
||||||
|
- tokenizer:
|
||||||
|
- ADMIN
|
||||||
|
summary: Patch role
|
||||||
|
tags:
|
||||||
|
- role
|
||||||
/api/v1/user:
|
/api/v1/user:
|
||||||
get:
|
get:
|
||||||
deprecated: false
|
deprecated: false
|
||||||
@@ -3888,6 +4109,25 @@ components:
|
|||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
RoletypesRole:
|
||||||
|
properties:
|
||||||
|
createdAt:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
orgId:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
updatedAt:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
TypesChangePasswordRequest:
|
TypesChangePasswordRequest:
|
||||||
properties:
|
properties:
|
||||||
newPassword:
|
newPassword:
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (provider *provider) Check(ctx context.Context, tuple *openfgav1.TupleKey)
|
|||||||
return provider.pkgAuthzService.Check(ctx, tuple)
|
return provider.pkgAuthzService.Check(ctx, tuple)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) CheckWithTupleCreation(ctx context.Context, claims authtypes.Claims, orgID valuer.UUID, relation authtypes.Relation, _ authtypes.Relation, typeable authtypes.Typeable, selectors []authtypes.Selector) error {
|
func (provider *provider) CheckWithTupleCreation(ctx context.Context, claims authtypes.Claims, orgID valuer.UUID, relation authtypes.Relation, typeable authtypes.Typeable, selectors []authtypes.Selector, _ []authtypes.Selector) error {
|
||||||
subject, err := authtypes.NewSubject(authtypes.TypeableUser, claims.UserID, orgID, nil)
|
subject, err := authtypes.NewSubject(authtypes.TypeableUser, claims.UserID, orgID, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -66,7 +66,7 @@ func (provider *provider) CheckWithTupleCreation(ctx context.Context, claims aut
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) CheckWithTupleCreationWithoutClaims(ctx context.Context, orgID valuer.UUID, relation authtypes.Relation, _ authtypes.Relation, typeable authtypes.Typeable, selectors []authtypes.Selector) error {
|
func (provider *provider) CheckWithTupleCreationWithoutClaims(ctx context.Context, orgID valuer.UUID, relation authtypes.Relation, typeable authtypes.Typeable, selectors []authtypes.Selector, _ []authtypes.Selector) error {
|
||||||
subject, err := authtypes.NewSubject(authtypes.TypeableAnonymous, authtypes.AnonymousUser.String(), orgID, nil)
|
subject, err := authtypes.NewSubject(authtypes.TypeableAnonymous, authtypes.AnonymousUser.String(), orgID, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -26,12 +26,13 @@ type module struct {
|
|||||||
pkgDashboardModule dashboard.Module
|
pkgDashboardModule dashboard.Module
|
||||||
store dashboardtypes.Store
|
store dashboardtypes.Store
|
||||||
settings factory.ScopedProviderSettings
|
settings factory.ScopedProviderSettings
|
||||||
role role.Module
|
roleSetter role.Setter
|
||||||
|
granter role.Granter
|
||||||
querier querier.Querier
|
querier querier.Querier
|
||||||
licensing licensing.Licensing
|
licensing licensing.Licensing
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModule(store dashboardtypes.Store, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, role role.Module, queryParser queryparser.QueryParser, querier querier.Querier, licensing licensing.Licensing) dashboard.Module {
|
func NewModule(store dashboardtypes.Store, settings factory.ProviderSettings, analytics analytics.Analytics, orgGetter organization.Getter, roleSetter role.Setter, granter role.Granter, queryParser queryparser.QueryParser, querier querier.Querier, licensing licensing.Licensing) dashboard.Module {
|
||||||
scopedProviderSettings := factory.NewScopedProviderSettings(settings, "github.com/SigNoz/signoz/ee/modules/dashboard/impldashboard")
|
scopedProviderSettings := factory.NewScopedProviderSettings(settings, "github.com/SigNoz/signoz/ee/modules/dashboard/impldashboard")
|
||||||
pkgDashboardModule := pkgimpldashboard.NewModule(store, settings, analytics, orgGetter, queryParser)
|
pkgDashboardModule := pkgimpldashboard.NewModule(store, settings, analytics, orgGetter, queryParser)
|
||||||
|
|
||||||
@@ -39,7 +40,8 @@ func NewModule(store dashboardtypes.Store, settings factory.ProviderSettings, an
|
|||||||
pkgDashboardModule: pkgDashboardModule,
|
pkgDashboardModule: pkgDashboardModule,
|
||||||
store: store,
|
store: store,
|
||||||
settings: scopedProviderSettings,
|
settings: scopedProviderSettings,
|
||||||
role: role,
|
roleSetter: roleSetter,
|
||||||
|
granter: granter,
|
||||||
querier: querier,
|
querier: querier,
|
||||||
licensing: licensing,
|
licensing: licensing,
|
||||||
}
|
}
|
||||||
@@ -59,12 +61,12 @@ func (module *module) CreatePublic(ctx context.Context, orgID valuer.UUID, publi
|
|||||||
return errors.Newf(errors.TypeAlreadyExists, dashboardtypes.ErrCodePublicDashboardAlreadyExists, "dashboard with id %s is already public", storablePublicDashboard.DashboardID)
|
return errors.Newf(errors.TypeAlreadyExists, dashboardtypes.ErrCodePublicDashboardAlreadyExists, "dashboard with id %s is already public", storablePublicDashboard.DashboardID)
|
||||||
}
|
}
|
||||||
|
|
||||||
role, err := module.role.GetOrCreate(ctx, roletypes.NewRole(roletypes.AnonymousUserRoleName, roletypes.AnonymousUserRoleDescription, roletypes.RoleTypeManaged.StringValue(), orgID))
|
role, err := module.roleSetter.GetOrCreate(ctx, orgID, roletypes.NewRole(roletypes.SigNozAnonymousRoleName, roletypes.SigNozAnonymousRoleDescription, roletypes.RoleTypeManaged, orgID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = module.role.Assign(ctx, role.ID, orgID, authtypes.MustNewSubject(authtypes.TypeableAnonymous, authtypes.AnonymousUser.StringValue(), orgID, nil))
|
err = module.granter.Grant(ctx, orgID, roletypes.SigNozAnonymousRoleName, authtypes.MustNewSubject(authtypes.TypeableAnonymous, authtypes.AnonymousUser.StringValue(), orgID, nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -77,7 +79,7 @@ func (module *module) CreatePublic(ctx context.Context, orgID valuer.UUID, publi
|
|||||||
authtypes.MustNewSelector(authtypes.TypeMetaResource, publicDashboard.ID.String()),
|
authtypes.MustNewSelector(authtypes.TypeMetaResource, publicDashboard.ID.String()),
|
||||||
)
|
)
|
||||||
|
|
||||||
err = module.role.PatchObjects(ctx, orgID, role.ID, authtypes.RelationRead, []*authtypes.Object{additionObject}, nil)
|
err = module.roleSetter.PatchObjects(ctx, orgID, role.ID, authtypes.RelationRead, []*authtypes.Object{additionObject}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -193,7 +195,7 @@ func (module *module) DeletePublic(ctx context.Context, orgID valuer.UUID, dashb
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
role, err := module.role.GetOrCreate(ctx, roletypes.NewRole(roletypes.AnonymousUserRoleName, roletypes.AnonymousUserRoleDescription, roletypes.RoleTypeManaged.StringValue(), orgID))
|
role, err := module.roleSetter.GetOrCreate(ctx, orgID, roletypes.NewRole(roletypes.SigNozAnonymousRoleName, roletypes.SigNozAnonymousRoleDescription, roletypes.RoleTypeManaged, orgID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -206,7 +208,7 @@ func (module *module) DeletePublic(ctx context.Context, orgID valuer.UUID, dashb
|
|||||||
authtypes.MustNewSelector(authtypes.TypeMetaResource, publicDashboard.ID.String()),
|
authtypes.MustNewSelector(authtypes.TypeMetaResource, publicDashboard.ID.String()),
|
||||||
)
|
)
|
||||||
|
|
||||||
err = module.role.PatchObjects(ctx, orgID, role.ID, authtypes.RelationRead, nil, []*authtypes.Object{deletionObject})
|
err = module.roleSetter.PatchObjects(ctx, orgID, role.ID, authtypes.RelationRead, nil, []*authtypes.Object{deletionObject})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -270,7 +272,7 @@ func (module *module) deletePublic(ctx context.Context, orgID valuer.UUID, dashb
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
role, err := module.role.GetOrCreate(ctx, roletypes.NewRole(roletypes.AnonymousUserRoleName, roletypes.AnonymousUserRoleDescription, roletypes.RoleTypeManaged.StringValue(), orgID))
|
role, err := module.roleSetter.GetOrCreate(ctx, orgID, roletypes.NewRole(roletypes.SigNozAnonymousRoleName, roletypes.SigNozAnonymousRoleDescription, roletypes.RoleTypeManaged, orgID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -283,7 +285,7 @@ func (module *module) deletePublic(ctx context.Context, orgID valuer.UUID, dashb
|
|||||||
authtypes.MustNewSelector(authtypes.TypeMetaResource, publicDashboard.ID.String()),
|
authtypes.MustNewSelector(authtypes.TypeMetaResource, publicDashboard.ID.String()),
|
||||||
)
|
)
|
||||||
|
|
||||||
err = module.role.PatchObjects(ctx, orgID, role.ID, authtypes.RelationRead, nil, []*authtypes.Object{deletionObject})
|
err = module.roleSetter.PatchObjects(ctx, orgID, role.ID, authtypes.RelationRead, nil, []*authtypes.Object{deletionObject})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
165
ee/modules/role/implrole/setter.go
Normal file
165
ee/modules/role/implrole/setter.go
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
package implrole
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/authz"
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/licensing"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type setter struct {
|
||||||
|
store roletypes.Store
|
||||||
|
authz authz.AuthZ
|
||||||
|
licensing licensing.Licensing
|
||||||
|
registry []role.RegisterTypeable
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSetter(store roletypes.Store, authz authz.AuthZ, licensing licensing.Licensing, registry []role.RegisterTypeable) role.Setter {
|
||||||
|
return &setter{
|
||||||
|
store: store,
|
||||||
|
authz: authz,
|
||||||
|
licensing: licensing,
|
||||||
|
registry: registry,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) Create(ctx context.Context, orgID valuer.UUID, role *roletypes.Role) error {
|
||||||
|
_, err := setter.licensing.GetActive(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return setter.store.Create(ctx, roletypes.NewStorableRoleFromRole(role))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) GetOrCreate(ctx context.Context, orgID valuer.UUID, role *roletypes.Role) (*roletypes.Role, error) {
|
||||||
|
_, err := setter.licensing.GetActive(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
existingRole, err := setter.store.GetByOrgIDAndName(ctx, role.OrgID, role.Name)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Ast(err, errors.TypeNotFound) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if existingRole != nil {
|
||||||
|
return roletypes.NewRoleFromStorableRole(existingRole), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setter.store.Create(ctx, roletypes.NewStorableRoleFromRole(role))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return role, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) GetResources(_ context.Context) []*authtypes.Resource {
|
||||||
|
typeables := make([]authtypes.Typeable, 0)
|
||||||
|
for _, register := range setter.registry {
|
||||||
|
typeables = append(typeables, register.MustGetTypeables()...)
|
||||||
|
}
|
||||||
|
// role module cannot self register itself!
|
||||||
|
typeables = append(typeables, setter.MustGetTypeables()...)
|
||||||
|
|
||||||
|
resources := make([]*authtypes.Resource, 0)
|
||||||
|
for _, typeable := range typeables {
|
||||||
|
resources = append(resources, &authtypes.Resource{Name: typeable.Name(), Type: typeable.Type()})
|
||||||
|
}
|
||||||
|
|
||||||
|
return resources
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) GetObjects(ctx context.Context, orgID valuer.UUID, id valuer.UUID, relation authtypes.Relation) ([]*authtypes.Object, error) {
|
||||||
|
storableRole, err := setter.store.Get(ctx, orgID, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
objects := make([]*authtypes.Object, 0)
|
||||||
|
for _, resource := range setter.GetResources(ctx) {
|
||||||
|
if slices.Contains(authtypes.TypeableRelations[resource.Type], relation) {
|
||||||
|
resourceObjects, err := setter.
|
||||||
|
authz.
|
||||||
|
ListObjects(
|
||||||
|
ctx,
|
||||||
|
authtypes.MustNewSubject(authtypes.TypeableRole, storableRole.ID.String(), orgID, &authtypes.RelationAssignee),
|
||||||
|
relation,
|
||||||
|
authtypes.MustNewTypeableFromType(resource.Type, resource.Name),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
objects = append(objects, resourceObjects...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return objects, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) Patch(ctx context.Context, orgID valuer.UUID, role *roletypes.Role) error {
|
||||||
|
_, err := setter.licensing.GetActive(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return setter.store.Update(ctx, orgID, roletypes.NewStorableRoleFromRole(role))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) PatchObjects(ctx context.Context, orgID valuer.UUID, id valuer.UUID, relation authtypes.Relation, additions, deletions []*authtypes.Object) error {
|
||||||
|
_, err := setter.licensing.GetActive(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
additionTuples, err := roletypes.GetAdditionTuples(id, orgID, relation, additions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
deletionTuples, err := roletypes.GetDeletionTuples(id, orgID, relation, deletions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setter.authz.Write(ctx, additionTuples, deletionTuples)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) Delete(ctx context.Context, orgID valuer.UUID, id valuer.UUID) error {
|
||||||
|
_, err := setter.licensing.GetActive(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "a valid license is not available").WithAdditional("this feature requires a valid license").WithAdditional(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
storableRole, err := setter.store.Get(ctx, orgID, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
role := roletypes.NewRoleFromStorableRole(storableRole)
|
||||||
|
err = role.CanEditDelete()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return setter.store.Delete(ctx, orgID, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) MustGetTypeables() []authtypes.Typeable {
|
||||||
|
return []authtypes.Typeable{authtypes.TypeableRole, roletypes.TypeableResourcesRoles}
|
||||||
|
}
|
||||||
@@ -211,7 +211,7 @@ func (s Server) HealthCheckStatus() chan healthcheck.Status {
|
|||||||
|
|
||||||
func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*http.Server, error) {
|
func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*http.Server, error) {
|
||||||
r := baseapp.NewRouter()
|
r := baseapp.NewRouter()
|
||||||
am := middleware.NewAuthZ(s.signoz.Instrumentation.Logger(), s.signoz.Modules.OrgGetter, s.signoz.Authz)
|
am := middleware.NewAuthZ(s.signoz.Instrumentation.Logger(), s.signoz.Modules.OrgGetter, s.signoz.Authz, s.signoz.Modules.RoleGetter)
|
||||||
|
|
||||||
r.Use(otelmux.Middleware(
|
r.Use(otelmux.Middleware(
|
||||||
"apiserver",
|
"apiserver",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func (provider *provider) addDashboardRoutes(router *mux.Router) error {
|
|||||||
ID: "CreatePublicDashboard",
|
ID: "CreatePublicDashboard",
|
||||||
Tags: []string{"dashboard"},
|
Tags: []string{"dashboard"},
|
||||||
Summary: "Create public dashboard",
|
Summary: "Create public dashboard",
|
||||||
Description: "This endpoints creates public sharing config and enables public sharing of the dashboard",
|
Description: "This endpoint creates public sharing config and enables public sharing of the dashboard",
|
||||||
Request: new(dashboardtypes.PostablePublicDashboard),
|
Request: new(dashboardtypes.PostablePublicDashboard),
|
||||||
RequestContentType: "",
|
RequestContentType: "",
|
||||||
Response: new(types.Identifiable),
|
Response: new(types.Identifiable),
|
||||||
@@ -34,7 +34,7 @@ func (provider *provider) addDashboardRoutes(router *mux.Router) error {
|
|||||||
ID: "GetPublicDashboard",
|
ID: "GetPublicDashboard",
|
||||||
Tags: []string{"dashboard"},
|
Tags: []string{"dashboard"},
|
||||||
Summary: "Get public dashboard",
|
Summary: "Get public dashboard",
|
||||||
Description: "This endpoints returns public sharing config for a dashboard",
|
Description: "This endpoint returns public sharing config for a dashboard",
|
||||||
Request: nil,
|
Request: nil,
|
||||||
RequestContentType: "",
|
RequestContentType: "",
|
||||||
Response: new(dashboardtypes.GettablePublicDasbhboard),
|
Response: new(dashboardtypes.GettablePublicDasbhboard),
|
||||||
@@ -51,7 +51,7 @@ func (provider *provider) addDashboardRoutes(router *mux.Router) error {
|
|||||||
ID: "UpdatePublicDashboard",
|
ID: "UpdatePublicDashboard",
|
||||||
Tags: []string{"dashboard"},
|
Tags: []string{"dashboard"},
|
||||||
Summary: "Update public dashboard",
|
Summary: "Update public dashboard",
|
||||||
Description: "This endpoints updates the public sharing config for a dashboard",
|
Description: "This endpoint updates the public sharing config for a dashboard",
|
||||||
Request: new(dashboardtypes.UpdatablePublicDashboard),
|
Request: new(dashboardtypes.UpdatablePublicDashboard),
|
||||||
RequestContentType: "",
|
RequestContentType: "",
|
||||||
Response: nil,
|
Response: nil,
|
||||||
@@ -68,7 +68,7 @@ func (provider *provider) addDashboardRoutes(router *mux.Router) error {
|
|||||||
ID: "DeletePublicDashboard",
|
ID: "DeletePublicDashboard",
|
||||||
Tags: []string{"dashboard"},
|
Tags: []string{"dashboard"},
|
||||||
Summary: "Delete public dashboard",
|
Summary: "Delete public dashboard",
|
||||||
Description: "This endpoints deletes the public sharing config and disables the public sharing of a dashboard",
|
Description: "This endpoint deletes the public sharing config and disables the public sharing of a dashboard",
|
||||||
Request: nil,
|
Request: nil,
|
||||||
RequestContentType: "",
|
RequestContentType: "",
|
||||||
Response: nil,
|
Response: nil,
|
||||||
@@ -83,7 +83,7 @@ func (provider *provider) addDashboardRoutes(router *mux.Router) error {
|
|||||||
|
|
||||||
if err := router.Handle("/api/v1/public/dashboards/{id}", handler.New(provider.authZ.CheckWithoutClaims(
|
if err := router.Handle("/api/v1/public/dashboards/{id}", handler.New(provider.authZ.CheckWithoutClaims(
|
||||||
provider.dashboardHandler.GetPublicData,
|
provider.dashboardHandler.GetPublicData,
|
||||||
authtypes.RelationRead, authtypes.RelationRead,
|
authtypes.RelationRead,
|
||||||
dashboardtypes.TypeableMetaResourcePublicDashboard,
|
dashboardtypes.TypeableMetaResourcePublicDashboard,
|
||||||
func(req *http.Request, orgs []*types.Organization) ([]authtypes.Selector, valuer.UUID, error) {
|
func(req *http.Request, orgs []*types.Organization) ([]authtypes.Selector, valuer.UUID, error) {
|
||||||
id, err := valuer.NewUUID(mux.Vars(req)["id"])
|
id, err := valuer.NewUUID(mux.Vars(req)["id"])
|
||||||
@@ -92,11 +92,11 @@ func (provider *provider) addDashboardRoutes(router *mux.Router) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return provider.dashboardModule.GetPublicDashboardSelectorsAndOrg(req.Context(), id, orgs)
|
return provider.dashboardModule.GetPublicDashboardSelectorsAndOrg(req.Context(), id, orgs)
|
||||||
}), handler.OpenAPIDef{
|
}, []string{}), handler.OpenAPIDef{
|
||||||
ID: "GetPublicDashboardData",
|
ID: "GetPublicDashboardData",
|
||||||
Tags: []string{"dashboard"},
|
Tags: []string{"dashboard"},
|
||||||
Summary: "Get public dashboard data",
|
Summary: "Get public dashboard data",
|
||||||
Description: "This endpoints returns the sanitized dashboard data for public access",
|
Description: "This endpoint returns the sanitized dashboard data for public access",
|
||||||
Request: nil,
|
Request: nil,
|
||||||
RequestContentType: "",
|
RequestContentType: "",
|
||||||
Response: new(dashboardtypes.GettablePublicDashboardData),
|
Response: new(dashboardtypes.GettablePublicDashboardData),
|
||||||
@@ -111,7 +111,7 @@ func (provider *provider) addDashboardRoutes(router *mux.Router) error {
|
|||||||
|
|
||||||
if err := router.Handle("/api/v1/public/dashboards/{id}/widgets/{idx}/query_range", handler.New(provider.authZ.CheckWithoutClaims(
|
if err := router.Handle("/api/v1/public/dashboards/{id}/widgets/{idx}/query_range", handler.New(provider.authZ.CheckWithoutClaims(
|
||||||
provider.dashboardHandler.GetPublicWidgetQueryRange,
|
provider.dashboardHandler.GetPublicWidgetQueryRange,
|
||||||
authtypes.RelationRead, authtypes.RelationRead,
|
authtypes.RelationRead,
|
||||||
dashboardtypes.TypeableMetaResourcePublicDashboard,
|
dashboardtypes.TypeableMetaResourcePublicDashboard,
|
||||||
func(req *http.Request, orgs []*types.Organization) ([]authtypes.Selector, valuer.UUID, error) {
|
func(req *http.Request, orgs []*types.Organization) ([]authtypes.Selector, valuer.UUID, error) {
|
||||||
id, err := valuer.NewUUID(mux.Vars(req)["id"])
|
id, err := valuer.NewUUID(mux.Vars(req)["id"])
|
||||||
@@ -120,7 +120,7 @@ func (provider *provider) addDashboardRoutes(router *mux.Router) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return provider.dashboardModule.GetPublicDashboardSelectorsAndOrg(req.Context(), id, orgs)
|
return provider.dashboardModule.GetPublicDashboardSelectorsAndOrg(req.Context(), id, orgs)
|
||||||
}), handler.OpenAPIDef{
|
}, []string{}), handler.OpenAPIDef{
|
||||||
ID: "GetPublicDashboardWidgetQueryRange",
|
ID: "GetPublicDashboardWidgetQueryRange",
|
||||||
Tags: []string{"dashboard"},
|
Tags: []string{"dashboard"},
|
||||||
Summary: "Get query range result",
|
Summary: "Get query range result",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ func (provider *provider) addGlobalRoutes(router *mux.Router) error {
|
|||||||
ID: "GetGlobalConfig",
|
ID: "GetGlobalConfig",
|
||||||
Tags: []string{"global"},
|
Tags: []string{"global"},
|
||||||
Summary: "Get global config",
|
Summary: "Get global config",
|
||||||
Description: "This endpoints returns global config",
|
Description: "This endpoint returns global config",
|
||||||
Request: nil,
|
Request: nil,
|
||||||
RequestContentType: "",
|
RequestContentType: "",
|
||||||
Response: new(types.GettableGlobalConfig),
|
Response: new(types.GettableGlobalConfig),
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/preference"
|
"github.com/SigNoz/signoz/pkg/modules/preference"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/promote"
|
"github.com/SigNoz/signoz/pkg/modules/promote"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/session"
|
"github.com/SigNoz/signoz/pkg/modules/session"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/user"
|
"github.com/SigNoz/signoz/pkg/modules/user"
|
||||||
"github.com/SigNoz/signoz/pkg/types"
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
@@ -41,6 +42,8 @@ type provider struct {
|
|||||||
dashboardHandler dashboard.Handler
|
dashboardHandler dashboard.Handler
|
||||||
metricsExplorerHandler metricsexplorer.Handler
|
metricsExplorerHandler metricsexplorer.Handler
|
||||||
gatewayHandler gateway.Handler
|
gatewayHandler gateway.Handler
|
||||||
|
roleGetter role.Getter
|
||||||
|
roleHandler role.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactory(
|
func NewFactory(
|
||||||
@@ -58,9 +61,11 @@ func NewFactory(
|
|||||||
dashboardHandler dashboard.Handler,
|
dashboardHandler dashboard.Handler,
|
||||||
metricsExplorerHandler metricsexplorer.Handler,
|
metricsExplorerHandler metricsexplorer.Handler,
|
||||||
gatewayHandler gateway.Handler,
|
gatewayHandler gateway.Handler,
|
||||||
|
roleGetter role.Getter,
|
||||||
|
roleHandler role.Handler,
|
||||||
) factory.ProviderFactory[apiserver.APIServer, apiserver.Config] {
|
) factory.ProviderFactory[apiserver.APIServer, apiserver.Config] {
|
||||||
return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, providerSettings factory.ProviderSettings, config apiserver.Config) (apiserver.APIServer, error) {
|
return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, providerSettings factory.ProviderSettings, config apiserver.Config) (apiserver.APIServer, error) {
|
||||||
return newProvider(ctx, providerSettings, config, orgGetter, authz, orgHandler, userHandler, sessionHandler, authDomainHandler, preferenceHandler, globalHandler, promoteHandler, flaggerHandler, dashboardModule, dashboardHandler, metricsExplorerHandler, gatewayHandler)
|
return newProvider(ctx, providerSettings, config, orgGetter, authz, orgHandler, userHandler, sessionHandler, authDomainHandler, preferenceHandler, globalHandler, promoteHandler, flaggerHandler, dashboardModule, dashboardHandler, metricsExplorerHandler, gatewayHandler, roleGetter, roleHandler)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +87,8 @@ func newProvider(
|
|||||||
dashboardHandler dashboard.Handler,
|
dashboardHandler dashboard.Handler,
|
||||||
metricsExplorerHandler metricsexplorer.Handler,
|
metricsExplorerHandler metricsexplorer.Handler,
|
||||||
gatewayHandler gateway.Handler,
|
gatewayHandler gateway.Handler,
|
||||||
|
roleGetter role.Getter,
|
||||||
|
roleHandler role.Handler,
|
||||||
) (apiserver.APIServer, error) {
|
) (apiserver.APIServer, error) {
|
||||||
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/apiserver/signozapiserver")
|
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/apiserver/signozapiserver")
|
||||||
router := mux.NewRouter().UseEncodedPath()
|
router := mux.NewRouter().UseEncodedPath()
|
||||||
@@ -102,9 +109,11 @@ func newProvider(
|
|||||||
dashboardHandler: dashboardHandler,
|
dashboardHandler: dashboardHandler,
|
||||||
metricsExplorerHandler: metricsExplorerHandler,
|
metricsExplorerHandler: metricsExplorerHandler,
|
||||||
gatewayHandler: gatewayHandler,
|
gatewayHandler: gatewayHandler,
|
||||||
|
roleGetter: roleGetter,
|
||||||
|
roleHandler: roleHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.authZ = middleware.NewAuthZ(settings.Logger(), orgGetter, authz)
|
provider.authZ = middleware.NewAuthZ(settings.Logger(), orgGetter, authz, roleGetter)
|
||||||
|
|
||||||
if err := provider.AddToRouter(router); err != nil {
|
if err := provider.AddToRouter(router); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -162,6 +171,10 @@ func (provider *provider) AddToRouter(router *mux.Router) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := provider.addRoleRoutes(router); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
99
pkg/apiserver/signozapiserver/role.go
Normal file
99
pkg/apiserver/signozapiserver/role.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package signozapiserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/http/handler"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||||
|
if err := router.Handle("/api/v1/roles", handler.New(provider.authZ.AdminAccess(provider.roleHandler.Create), handler.OpenAPIDef{
|
||||||
|
ID: "CreateRole",
|
||||||
|
Tags: []string{"role"},
|
||||||
|
Summary: "Create role",
|
||||||
|
Description: "This endpoint creates a role",
|
||||||
|
Request: nil,
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: new(types.Identifiable),
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusCreated,
|
||||||
|
ErrorStatusCodes: []int{},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Handle("/api/v1/roles", handler.New(provider.authZ.AdminAccess(provider.roleHandler.List), handler.OpenAPIDef{
|
||||||
|
ID: "ListRoles",
|
||||||
|
Tags: []string{"role"},
|
||||||
|
Summary: "List roles",
|
||||||
|
Description: "This endpoint lists all roles",
|
||||||
|
Request: nil,
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: make([]*roletypes.Role, 0),
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusOK,
|
||||||
|
ErrorStatusCodes: []int{},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authZ.AdminAccess(provider.roleHandler.Get), handler.OpenAPIDef{
|
||||||
|
ID: "GetRole",
|
||||||
|
Tags: []string{"role"},
|
||||||
|
Summary: "Get role",
|
||||||
|
Description: "This endpoint gets a role",
|
||||||
|
Request: nil,
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: new(roletypes.Role),
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusOK,
|
||||||
|
ErrorStatusCodes: []int{},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authZ.AdminAccess(provider.roleHandler.Patch), handler.OpenAPIDef{
|
||||||
|
ID: "PatchRole",
|
||||||
|
Tags: []string{"role"},
|
||||||
|
Summary: "Patch role",
|
||||||
|
Description: "This endpoint patches a role",
|
||||||
|
Request: nil,
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: nil,
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusNoContent,
|
||||||
|
ErrorStatusCodes: []int{},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodPatch).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authZ.AdminAccess(provider.roleHandler.Delete), handler.OpenAPIDef{
|
||||||
|
ID: "DeleteRole",
|
||||||
|
Tags: []string{"role"},
|
||||||
|
Summary: "Delete role",
|
||||||
|
Description: "This endpoint deletes a role",
|
||||||
|
Request: nil,
|
||||||
|
RequestContentType: "",
|
||||||
|
Response: nil,
|
||||||
|
ResponseContentType: "application/json",
|
||||||
|
SuccessStatusCode: http.StatusNoContent,
|
||||||
|
ErrorStatusCodes: []int{},
|
||||||
|
Deprecated: false,
|
||||||
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
|
})).Methods(http.MethodDelete).GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -16,9 +16,10 @@ type AuthZ interface {
|
|||||||
Check(context.Context, *openfgav1.TupleKey) error
|
Check(context.Context, *openfgav1.TupleKey) error
|
||||||
|
|
||||||
// CheckWithTupleCreation takes upon the responsibility for generating the tuples alongside everything Check does.
|
// CheckWithTupleCreation takes upon the responsibility for generating the tuples alongside everything Check does.
|
||||||
CheckWithTupleCreation(context.Context, authtypes.Claims, valuer.UUID, authtypes.Relation, authtypes.Relation, authtypes.Typeable, []authtypes.Selector) error
|
CheckWithTupleCreation(context.Context, authtypes.Claims, valuer.UUID, authtypes.Relation, authtypes.Typeable, []authtypes.Selector, []authtypes.Selector) error
|
||||||
|
|
||||||
CheckWithTupleCreationWithoutClaims(context.Context, valuer.UUID, authtypes.Relation, authtypes.Relation, authtypes.Typeable, []authtypes.Selector) error
|
// CheckWithTupleCreationWithoutClaims checks permissions for anonymous users.
|
||||||
|
CheckWithTupleCreationWithoutClaims(context.Context, valuer.UUID, authtypes.Relation, authtypes.Typeable, []authtypes.Selector, []authtypes.Selector) error
|
||||||
|
|
||||||
// Batch Check returns error when the upstream authorization server is unavailable or for all the tuples of subject (s) doesn't have relation (r) on object (o).
|
// Batch Check returns error when the upstream authorization server is unavailable or for all the tuples of subject (s) doesn't have relation (r) on object (o).
|
||||||
BatchCheck(context.Context, []*openfgav1.TupleKey) error
|
BatchCheck(context.Context, []*openfgav1.TupleKey) error
|
||||||
|
|||||||
@@ -152,17 +152,17 @@ func (provider *provider) BatchCheck(ctx context.Context, tupleReq []*openfgav1.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "")
|
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "none of the subjects are allowed for requested access")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) CheckWithTupleCreation(ctx context.Context, claims authtypes.Claims, orgID valuer.UUID, _ authtypes.Relation, translation authtypes.Relation, _ authtypes.Typeable, _ []authtypes.Selector) error {
|
func (provider *provider) CheckWithTupleCreation(ctx context.Context, claims authtypes.Claims, orgID valuer.UUID, _ authtypes.Relation, _ authtypes.Typeable, _ []authtypes.Selector, roleSelectors []authtypes.Selector) error {
|
||||||
subject, err := authtypes.NewSubject(authtypes.TypeableUser, claims.UserID, orgID, nil)
|
subject, err := authtypes.NewSubject(authtypes.TypeableUser, claims.UserID, orgID, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tuples, err := authtypes.TypeableOrganization.Tuples(subject, translation, []authtypes.Selector{authtypes.MustNewSelector(authtypes.TypeOrganization, orgID.StringValue())}, orgID)
|
tuples, err := authtypes.TypeableRole.Tuples(subject, authtypes.RelationAssignee, roleSelectors, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -175,13 +175,13 @@ func (provider *provider) CheckWithTupleCreation(ctx context.Context, claims aut
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) CheckWithTupleCreationWithoutClaims(ctx context.Context, orgID valuer.UUID, _ authtypes.Relation, translation authtypes.Relation, _ authtypes.Typeable, _ []authtypes.Selector) error {
|
func (provider *provider) CheckWithTupleCreationWithoutClaims(ctx context.Context, orgID valuer.UUID, _ authtypes.Relation, _ authtypes.Typeable, _ []authtypes.Selector, roleSelectors []authtypes.Selector) error {
|
||||||
subject, err := authtypes.NewSubject(authtypes.TypeableAnonymous, authtypes.AnonymousUser.String(), orgID, nil)
|
subject, err := authtypes.NewSubject(authtypes.TypeableAnonymous, authtypes.AnonymousUser.String(), orgID, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tuples, err := authtypes.TypeableOrganization.Tuples(subject, translation, []authtypes.Selector{authtypes.MustNewSelector(authtypes.TypeOrganization, orgID.StringValue())}, orgID)
|
tuples, err := authtypes.TypeableRole.Tuples(subject, authtypes.RelationAssignee, roleSelectors, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -195,6 +195,10 @@ func (provider *provider) CheckWithTupleCreationWithoutClaims(ctx context.Contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) Write(ctx context.Context, additions []*openfgav1.TupleKey, deletions []*openfgav1.TupleKey) error {
|
func (provider *provider) Write(ctx context.Context, additions []*openfgav1.TupleKey, deletions []*openfgav1.TupleKey) error {
|
||||||
|
if len(additions) == 0 && len(deletions) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
storeID, modelID := provider.getStoreIDandModelID()
|
storeID, modelID := provider.getStoreIDandModelID()
|
||||||
deletionTuplesWithoutCondition := make([]*openfgav1.TupleKeyWithoutCondition, len(deletions))
|
deletionTuplesWithoutCondition := make([]*openfgav1.TupleKeyWithoutCondition, len(deletions))
|
||||||
for idx, tuple := range deletions {
|
for idx, tuple := range deletions {
|
||||||
|
|||||||
@@ -34,11 +34,11 @@ func TestProviderStartStop(t *testing.T) {
|
|||||||
sqlstore.Mock().ExpectQuery("SELECT authorization_model_id, schema_version, type, type_definition, serialized_protobuf FROM authorization_model WHERE authorization_model_id = (.+) AND store = (.+)").WithArgs("01K44QQKXR6F729W160NFCJT58", "01K3V0NTN47MPTMEV1PD5ST6ZC").WillReturnRows(modelRows)
|
sqlstore.Mock().ExpectQuery("SELECT authorization_model_id, schema_version, type, type_definition, serialized_protobuf FROM authorization_model WHERE authorization_model_id = (.+) AND store = (.+)").WithArgs("01K44QQKXR6F729W160NFCJT58", "01K3V0NTN47MPTMEV1PD5ST6ZC").WillReturnRows(modelRows)
|
||||||
|
|
||||||
sqlstore.Mock().ExpectExec("INSERT INTO authorization_model (.+) VALUES (.+)").WillReturnResult(sqlmock.NewResult(1, 1))
|
sqlstore.Mock().ExpectExec("INSERT INTO authorization_model (.+) VALUES (.+)").WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := provider.Start(context.Background())
|
err := provider.Start(context.Background())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// wait for the service to start
|
// wait for the service to start
|
||||||
time.Sleep(time.Second * 2)
|
time.Sleep(time.Second * 2)
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/authz"
|
"github.com/SigNoz/signoz/pkg/authz"
|
||||||
"github.com/SigNoz/signoz/pkg/http/render"
|
"github.com/SigNoz/signoz/pkg/http/render"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role"
|
||||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
@@ -20,14 +21,15 @@ type AuthZ struct {
|
|||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
orgGetter organization.Getter
|
orgGetter organization.Getter
|
||||||
authzService authz.AuthZ
|
authzService authz.AuthZ
|
||||||
|
roleGetter role.Getter
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthZ(logger *slog.Logger, orgGetter organization.Getter, authzService authz.AuthZ) *AuthZ {
|
func NewAuthZ(logger *slog.Logger, orgGetter organization.Getter, authzService authz.AuthZ, roleGetter role.Getter) *AuthZ {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
panic("cannot build authz middleware, logger is empty")
|
panic("cannot build authz middleware, logger is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &AuthZ{logger: logger, orgGetter: orgGetter, authzService: authzService}
|
return &AuthZ{logger: logger, orgGetter: orgGetter, authzService: authzService, roleGetter: roleGetter}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (middleware *AuthZ) ViewAccess(next http.HandlerFunc) http.HandlerFunc {
|
func (middleware *AuthZ) ViewAccess(next http.HandlerFunc) http.HandlerFunc {
|
||||||
@@ -109,9 +111,10 @@ func (middleware *AuthZ) OpenAccess(next http.HandlerFunc) http.HandlerFunc {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (middleware *AuthZ) Check(next http.HandlerFunc, relation authtypes.Relation, translation authtypes.Relation, typeable authtypes.Typeable, cb authtypes.SelectorCallbackWithClaimsFn) http.HandlerFunc {
|
func (middleware *AuthZ) Check(next http.HandlerFunc, relation authtypes.Relation, typeable authtypes.Typeable, cb authtypes.SelectorCallbackWithClaimsFn, roles []string) http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
claims, err := authtypes.ClaimsFromContext(req.Context())
|
ctx := req.Context()
|
||||||
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
return
|
return
|
||||||
@@ -129,7 +132,18 @@ func (middleware *AuthZ) Check(next http.HandlerFunc, relation authtypes.Relatio
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = middleware.authzService.CheckWithTupleCreation(req.Context(), claims, orgId, relation, translation, typeable, selectors)
|
roles, err := middleware.roleGetter.ListByOrgIDAndNames(req.Context(), orgId, roles)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
roleSelectors := []authtypes.Selector{}
|
||||||
|
for _, role := range roles {
|
||||||
|
selectors = append(selectors, authtypes.MustNewSelector(authtypes.TypeRole, role.ID.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = middleware.authzService.CheckWithTupleCreation(ctx, claims, orgId, relation, typeable, selectors, roleSelectors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
return
|
return
|
||||||
@@ -139,7 +153,7 @@ func (middleware *AuthZ) Check(next http.HandlerFunc, relation authtypes.Relatio
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (middleware *AuthZ) CheckWithoutClaims(next http.HandlerFunc, relation authtypes.Relation, translation authtypes.Relation, typeable authtypes.Typeable, cb authtypes.SelectorCallbackWithoutClaimsFn) http.HandlerFunc {
|
func (middleware *AuthZ) CheckWithoutClaims(next http.HandlerFunc, relation authtypes.Relation, typeable authtypes.Typeable, cb authtypes.SelectorCallbackWithoutClaimsFn, roles []string) http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
ctx := req.Context()
|
ctx := req.Context()
|
||||||
orgs, err := middleware.orgGetter.ListByOwnedKeyRange(ctx)
|
orgs, err := middleware.orgGetter.ListByOwnedKeyRange(ctx)
|
||||||
@@ -154,7 +168,7 @@ func (middleware *AuthZ) CheckWithoutClaims(next http.HandlerFunc, relation auth
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = middleware.authzService.CheckWithTupleCreationWithoutClaims(ctx, orgID, relation, translation, typeable, selectors)
|
err = middleware.authzService.CheckWithTupleCreationWithoutClaims(ctx, orgID, relation, typeable, selectors, selectors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
return
|
return
|
||||||
|
|||||||
63
pkg/modules/role/implrole/getter.go
Normal file
63
pkg/modules/role/implrole/getter.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package implrole
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type getter struct {
|
||||||
|
store roletypes.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGetter(store roletypes.Store) role.Getter {
|
||||||
|
return &getter{store: store}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (getter *getter) Get(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*roletypes.Role, error) {
|
||||||
|
storableRole, err := getter.store.Get(ctx, orgID, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return roletypes.NewRoleFromStorableRole(storableRole), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (getter *getter) GetByOrgIDAndName(ctx context.Context, orgID valuer.UUID, name string) (*roletypes.Role, error) {
|
||||||
|
storableRole, err := getter.store.GetByOrgIDAndName(ctx, orgID, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return roletypes.NewRoleFromStorableRole(storableRole), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (getter *getter) List(ctx context.Context, orgID valuer.UUID) ([]*roletypes.Role, error) {
|
||||||
|
storableRoles, err := getter.store.List(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
roles := make([]*roletypes.Role, len(storableRoles))
|
||||||
|
for idx, storableRole := range storableRoles {
|
||||||
|
roles[idx] = roletypes.NewRoleFromStorableRole(storableRole)
|
||||||
|
}
|
||||||
|
|
||||||
|
return roles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (getter *getter) ListByOrgIDAndNames(ctx context.Context, orgID valuer.UUID, names []string) ([]*roletypes.Role, error) {
|
||||||
|
storableRoles, err := getter.store.ListByOrgIDAndNames(ctx, orgID, names)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
roles := make([]*roletypes.Role, len(storableRoles))
|
||||||
|
for idx, storable := range storableRoles {
|
||||||
|
roles[idx] = roletypes.NewRoleFromStorableRole(storable)
|
||||||
|
}
|
||||||
|
|
||||||
|
return roles, nil
|
||||||
|
}
|
||||||
108
pkg/modules/role/implrole/granter.go
Normal file
108
pkg/modules/role/implrole/granter.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package implrole
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/authz"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type granter struct {
|
||||||
|
store roletypes.Store
|
||||||
|
authz authz.AuthZ
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGranter(store roletypes.Store, authz authz.AuthZ) role.Granter {
|
||||||
|
return &granter{store: store, authz: authz}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (granter *granter) Grant(ctx context.Context, orgID valuer.UUID, name string, subject string) error {
|
||||||
|
role, err := granter.store.GetByOrgIDAndName(ctx, orgID, name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tuples, err := authtypes.TypeableRole.Tuples(
|
||||||
|
subject,
|
||||||
|
authtypes.RelationAssignee,
|
||||||
|
[]authtypes.Selector{
|
||||||
|
authtypes.MustNewSelector(authtypes.TypeRole, role.ID.StringValue()),
|
||||||
|
},
|
||||||
|
orgID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return granter.authz.Write(ctx, tuples, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (granter *granter) GrantByID(ctx context.Context, orgID valuer.UUID, id valuer.UUID, subject string) error {
|
||||||
|
tuples, err := authtypes.TypeableRole.Tuples(
|
||||||
|
subject,
|
||||||
|
authtypes.RelationAssignee,
|
||||||
|
[]authtypes.Selector{
|
||||||
|
authtypes.MustNewSelector(authtypes.TypeRole, id.StringValue()),
|
||||||
|
},
|
||||||
|
orgID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return granter.authz.Write(ctx, tuples, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (granter *granter) ModifyGrant(ctx context.Context, orgID valuer.UUID, existingRoleName string, updatedRoleName string, subject string) error {
|
||||||
|
err := granter.Revoke(ctx, orgID, existingRoleName, subject)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = granter.Grant(ctx, orgID, updatedRoleName, subject)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (granter *granter) Revoke(ctx context.Context, orgID valuer.UUID, name string, subject string) error {
|
||||||
|
role, err := granter.store.GetByOrgIDAndName(ctx, orgID, name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tuples, err := authtypes.TypeableRole.Tuples(
|
||||||
|
subject,
|
||||||
|
authtypes.RelationAssignee,
|
||||||
|
[]authtypes.Selector{
|
||||||
|
authtypes.MustNewSelector(authtypes.TypeRole, role.ID.StringValue()),
|
||||||
|
},
|
||||||
|
orgID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return granter.authz.Write(ctx, nil, tuples)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (granter *granter) CreateManagedRoles(ctx context.Context, _ valuer.UUID, managedRoles []*roletypes.Role) error {
|
||||||
|
err := granter.store.RunInTx(ctx, func(ctx context.Context) error {
|
||||||
|
for _, role := range managedRoles {
|
||||||
|
err := granter.store.Create(ctx, roletypes.NewStorableRoleFromRole(role))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -14,11 +14,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type handler struct {
|
type handler struct {
|
||||||
module role.Module
|
setter role.Setter
|
||||||
|
getter role.Getter
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler(module role.Module) role.Handler {
|
func NewHandler(setter role.Setter, getter role.Getter) role.Handler {
|
||||||
return &handler{module: module}
|
return &handler{setter: setter, getter: getter}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *handler) Create(rw http.ResponseWriter, r *http.Request) {
|
func (handler *handler) Create(rw http.ResponseWriter, r *http.Request) {
|
||||||
@@ -35,7 +36,7 @@ func (handler *handler) Create(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = handler.module.Create(ctx, roletypes.NewRole(req.Name, req.Description, roletypes.RoleTypeCustom.StringValue(), valuer.MustNewUUID(claims.OrgID)))
|
err = handler.setter.Create(ctx, valuer.MustNewUUID(claims.OrgID), roletypes.NewRole(req.Name, req.Description, roletypes.RoleTypeCustom, valuer.MustNewUUID(claims.OrgID)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
return
|
return
|
||||||
@@ -63,7 +64,7 @@ func (handler *handler) Get(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
role, err := handler.module.Get(ctx, valuer.MustNewUUID(claims.OrgID), roleID)
|
role, err := handler.getter.Get(ctx, valuer.MustNewUUID(claims.OrgID), roleID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
return
|
return
|
||||||
@@ -102,7 +103,7 @@ func (handler *handler) GetObjects(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
objects, err := handler.module.GetObjects(ctx, valuer.MustNewUUID(claims.OrgID), roleID, relation)
|
objects, err := handler.setter.GetObjects(ctx, valuer.MustNewUUID(claims.OrgID), roleID, relation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
return
|
return
|
||||||
@@ -113,7 +114,7 @@ func (handler *handler) GetObjects(rw http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (handler *handler) GetResources(rw http.ResponseWriter, r *http.Request) {
|
func (handler *handler) GetResources(rw http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
resources := handler.module.GetResources(ctx)
|
resources := handler.setter.GetResources(ctx)
|
||||||
|
|
||||||
var resourceRelations = struct {
|
var resourceRelations = struct {
|
||||||
Resources []*authtypes.Resource `json:"resources"`
|
Resources []*authtypes.Resource `json:"resources"`
|
||||||
@@ -133,7 +134,7 @@ func (handler *handler) List(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
roles, err := handler.module.List(ctx, valuer.MustNewUUID(claims.OrgID))
|
roles, err := handler.getter.List(ctx, valuer.MustNewUUID(claims.OrgID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
return
|
return
|
||||||
@@ -162,14 +163,19 @@ func (handler *handler) Patch(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
role, err := handler.module.Get(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
role, err := handler.getter.Get(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
role.PatchMetadata(req.Name, req.Description)
|
err = role.PatchMetadata(req.Name, req.Description)
|
||||||
err = handler.module.Patch(ctx, valuer.MustNewUUID(claims.OrgID), role)
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handler.setter.Patch(ctx, valuer.MustNewUUID(claims.OrgID), role)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
return
|
return
|
||||||
@@ -204,13 +210,19 @@ func (handler *handler) PatchObjects(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
patchableObjects, err := roletypes.NewPatchableObjects(req.Additions, req.Deletions, relation)
|
role, err := handler.getter.Get(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = handler.module.PatchObjects(ctx, valuer.MustNewUUID(claims.OrgID), id, relation, patchableObjects.Additions, patchableObjects.Deletions)
|
patchableObjects, err := role.NewPatchableObjects(req.Additions, req.Deletions, relation)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handler.setter.PatchObjects(ctx, valuer.MustNewUUID(claims.OrgID), id, relation, patchableObjects.Additions, patchableObjects.Deletions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
return
|
return
|
||||||
@@ -233,7 +245,7 @@ func (handler *handler) Delete(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = handler.module.Delete(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
err = handler.setter.Delete(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,164 +0,0 @@
|
|||||||
package implrole
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/authz"
|
|
||||||
"github.com/SigNoz/signoz/pkg/errors"
|
|
||||||
"github.com/SigNoz/signoz/pkg/modules/role"
|
|
||||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
|
||||||
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
|
||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type module struct {
|
|
||||||
store roletypes.Store
|
|
||||||
registry []role.RegisterTypeable
|
|
||||||
authz authz.AuthZ
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewModule(store roletypes.Store, authz authz.AuthZ, registry []role.RegisterTypeable) role.Module {
|
|
||||||
return &module{
|
|
||||||
store: store,
|
|
||||||
authz: authz,
|
|
||||||
registry: registry,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (module *module) Create(ctx context.Context, role *roletypes.Role) error {
|
|
||||||
return module.store.Create(ctx, roletypes.NewStorableRoleFromRole(role))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (module *module) GetOrCreate(ctx context.Context, role *roletypes.Role) (*roletypes.Role, error) {
|
|
||||||
existingRole, err := module.store.GetByNameAndOrgID(ctx, role.Name, role.OrgID)
|
|
||||||
if err != nil {
|
|
||||||
if !errors.Ast(err, errors.TypeNotFound) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if existingRole != nil {
|
|
||||||
return roletypes.NewRoleFromStorableRole(existingRole), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err = module.store.Create(ctx, roletypes.NewStorableRoleFromRole(role))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return role, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (module *module) GetResources(_ context.Context) []*authtypes.Resource {
|
|
||||||
typeables := make([]authtypes.Typeable, 0)
|
|
||||||
for _, register := range module.registry {
|
|
||||||
typeables = append(typeables, register.MustGetTypeables()...)
|
|
||||||
}
|
|
||||||
// role module cannot self register itself!
|
|
||||||
typeables = append(typeables, module.MustGetTypeables()...)
|
|
||||||
|
|
||||||
resources := make([]*authtypes.Resource, 0)
|
|
||||||
for _, typeable := range typeables {
|
|
||||||
resources = append(resources, &authtypes.Resource{Name: typeable.Name(), Type: typeable.Type()})
|
|
||||||
}
|
|
||||||
|
|
||||||
return resources
|
|
||||||
}
|
|
||||||
|
|
||||||
func (module *module) Get(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*roletypes.Role, error) {
|
|
||||||
storableRole, err := module.store.Get(ctx, orgID, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return roletypes.NewRoleFromStorableRole(storableRole), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (module *module) GetObjects(ctx context.Context, orgID valuer.UUID, id valuer.UUID, relation authtypes.Relation) ([]*authtypes.Object, error) {
|
|
||||||
storableRole, err := module.store.Get(ctx, orgID, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
objects := make([]*authtypes.Object, 0)
|
|
||||||
for _, resource := range module.GetResources(ctx) {
|
|
||||||
if slices.Contains(authtypes.TypeableRelations[resource.Type], relation) {
|
|
||||||
resourceObjects, err := module.
|
|
||||||
authz.
|
|
||||||
ListObjects(
|
|
||||||
ctx,
|
|
||||||
authtypes.MustNewSubject(authtypes.TypeableRole, storableRole.ID.String(), orgID, &authtypes.RelationAssignee),
|
|
||||||
relation,
|
|
||||||
authtypes.MustNewTypeableFromType(resource.Type, resource.Name),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
objects = append(objects, resourceObjects...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return objects, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (module *module) List(ctx context.Context, orgID valuer.UUID) ([]*roletypes.Role, error) {
|
|
||||||
storableRoles, err := module.store.List(ctx, orgID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
roles := make([]*roletypes.Role, len(storableRoles))
|
|
||||||
for idx, storableRole := range storableRoles {
|
|
||||||
roles[idx] = roletypes.NewRoleFromStorableRole(storableRole)
|
|
||||||
}
|
|
||||||
|
|
||||||
return roles, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (module *module) Patch(ctx context.Context, orgID valuer.UUID, role *roletypes.Role) error {
|
|
||||||
return module.store.Update(ctx, orgID, roletypes.NewStorableRoleFromRole(role))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (module *module) PatchObjects(ctx context.Context, orgID valuer.UUID, id valuer.UUID, relation authtypes.Relation, additions, deletions []*authtypes.Object) error {
|
|
||||||
additionTuples, err := roletypes.GetAdditionTuples(id, orgID, relation, additions)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
deletionTuples, err := roletypes.GetDeletionTuples(id, orgID, relation, deletions)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = module.authz.Write(ctx, additionTuples, deletionTuples)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (module *module) Assign(ctx context.Context, id valuer.UUID, orgID valuer.UUID, subject string) error {
|
|
||||||
tuples, err := authtypes.TypeableRole.Tuples(
|
|
||||||
subject,
|
|
||||||
authtypes.RelationAssignee,
|
|
||||||
[]authtypes.Selector{
|
|
||||||
authtypes.MustNewSelector(authtypes.TypeRole, id.StringValue()),
|
|
||||||
},
|
|
||||||
orgID,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return module.authz.Write(ctx, tuples, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (module *module) Delete(ctx context.Context, orgID valuer.UUID, id valuer.UUID) error {
|
|
||||||
return module.store.Delete(ctx, orgID, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (module *module) MustGetTypeables() []authtypes.Typeable {
|
|
||||||
return []authtypes.Typeable{authtypes.TypeableRole, roletypes.TypeableResourcesRoles}
|
|
||||||
}
|
|
||||||
53
pkg/modules/role/implrole/setter.go
Normal file
53
pkg/modules/role/implrole/setter.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package implrole
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/authz"
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type setter struct {
|
||||||
|
store roletypes.Store
|
||||||
|
authz authz.AuthZ
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSetter(store roletypes.Store, authz authz.AuthZ) role.Setter {
|
||||||
|
return &setter{store: store, authz: authz}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) Create(_ context.Context, _ valuer.UUID, _ *roletypes.Role) error {
|
||||||
|
return errors.Newf(errors.TypeUnsupported, roletypes.ErrCodeRoleUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) GetOrCreate(_ context.Context, _ valuer.UUID, _ *roletypes.Role) (*roletypes.Role, error) {
|
||||||
|
return nil, errors.Newf(errors.TypeUnsupported, roletypes.ErrCodeRoleUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) GetResources(_ context.Context) []*authtypes.Resource {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) GetObjects(ctx context.Context, orgID valuer.UUID, id valuer.UUID, relation authtypes.Relation) ([]*authtypes.Object, error) {
|
||||||
|
return nil, errors.Newf(errors.TypeUnsupported, roletypes.ErrCodeRoleUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) Patch(_ context.Context, _ valuer.UUID, _ *roletypes.Role) error {
|
||||||
|
return errors.Newf(errors.TypeUnsupported, roletypes.ErrCodeRoleUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) PatchObjects(_ context.Context, _ valuer.UUID, _ valuer.UUID, _ authtypes.Relation, _, _ []*authtypes.Object) error {
|
||||||
|
return errors.Newf(errors.TypeUnsupported, roletypes.ErrCodeRoleUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) Delete(_ context.Context, _ valuer.UUID, _ valuer.UUID) error {
|
||||||
|
return errors.Newf(errors.TypeUnsupported, roletypes.ErrCodeRoleUnsupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setter *setter) MustGetTypeables() []authtypes.Typeable {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type store struct {
|
type store struct {
|
||||||
@@ -20,7 +21,7 @@ func NewStore(sqlstore sqlstore.SQLStore) roletypes.Store {
|
|||||||
func (store *store) Create(ctx context.Context, role *roletypes.StorableRole) error {
|
func (store *store) Create(ctx context.Context, role *roletypes.StorableRole) error {
|
||||||
_, err := store.
|
_, err := store.
|
||||||
sqlstore.
|
sqlstore.
|
||||||
BunDB().
|
BunDBCtx(ctx).
|
||||||
NewInsert().
|
NewInsert().
|
||||||
Model(role).
|
Model(role).
|
||||||
Exec(ctx)
|
Exec(ctx)
|
||||||
@@ -35,7 +36,7 @@ func (store *store) Get(ctx context.Context, orgID valuer.UUID, id valuer.UUID)
|
|||||||
role := new(roletypes.StorableRole)
|
role := new(roletypes.StorableRole)
|
||||||
err := store.
|
err := store.
|
||||||
sqlstore.
|
sqlstore.
|
||||||
BunDB().
|
BunDBCtx(ctx).
|
||||||
NewSelect().
|
NewSelect().
|
||||||
Model(role).
|
Model(role).
|
||||||
Where("org_id = ?", orgID).
|
Where("org_id = ?", orgID).
|
||||||
@@ -48,11 +49,11 @@ func (store *store) Get(ctx context.Context, orgID valuer.UUID, id valuer.UUID)
|
|||||||
return role, nil
|
return role, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *store) GetByNameAndOrgID(ctx context.Context, name string, orgID valuer.UUID) (*roletypes.StorableRole, error) {
|
func (store *store) GetByOrgIDAndName(ctx context.Context, orgID valuer.UUID, name string) (*roletypes.StorableRole, error) {
|
||||||
role := new(roletypes.StorableRole)
|
role := new(roletypes.StorableRole)
|
||||||
err := store.
|
err := store.
|
||||||
sqlstore.
|
sqlstore.
|
||||||
BunDB().
|
BunDBCtx(ctx).
|
||||||
NewSelect().
|
NewSelect().
|
||||||
Model(role).
|
Model(role).
|
||||||
Where("org_id = ?", orgID).
|
Where("org_id = ?", orgID).
|
||||||
@@ -69,13 +70,30 @@ func (store *store) List(ctx context.Context, orgID valuer.UUID) ([]*roletypes.S
|
|||||||
roles := make([]*roletypes.StorableRole, 0)
|
roles := make([]*roletypes.StorableRole, 0)
|
||||||
err := store.
|
err := store.
|
||||||
sqlstore.
|
sqlstore.
|
||||||
BunDB().
|
BunDBCtx(ctx).
|
||||||
NewSelect().
|
NewSelect().
|
||||||
Model(&roles).
|
Model(&roles).
|
||||||
Where("org_id = ?", orgID).
|
Where("org_id = ?", orgID).
|
||||||
Scan(ctx)
|
Scan(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, store.sqlstore.WrapNotFoundErrf(err, roletypes.ErrCodeRoleNotFound, "no roles found in org_id: %s", orgID)
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return roles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) ListByOrgIDAndNames(ctx context.Context, orgID valuer.UUID, names []string) ([]*roletypes.StorableRole, error) {
|
||||||
|
roles := make([]*roletypes.StorableRole, 0)
|
||||||
|
err := store.
|
||||||
|
sqlstore.
|
||||||
|
BunDBCtx(ctx).
|
||||||
|
NewSelect().
|
||||||
|
Model(&roles).
|
||||||
|
Where("org_id = ?", orgID).
|
||||||
|
Where("name IN (?)", bun.In(names)).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return roles, nil
|
return roles, nil
|
||||||
@@ -84,7 +102,7 @@ func (store *store) List(ctx context.Context, orgID valuer.UUID) ([]*roletypes.S
|
|||||||
func (store *store) Update(ctx context.Context, orgID valuer.UUID, role *roletypes.StorableRole) error {
|
func (store *store) Update(ctx context.Context, orgID valuer.UUID, role *roletypes.StorableRole) error {
|
||||||
_, err := store.
|
_, err := store.
|
||||||
sqlstore.
|
sqlstore.
|
||||||
BunDB().
|
BunDBCtx(ctx).
|
||||||
NewUpdate().
|
NewUpdate().
|
||||||
Model(role).
|
Model(role).
|
||||||
WherePK().
|
WherePK().
|
||||||
@@ -100,7 +118,7 @@ func (store *store) Update(ctx context.Context, orgID valuer.UUID, role *roletyp
|
|||||||
func (store *store) Delete(ctx context.Context, orgID valuer.UUID, id valuer.UUID) error {
|
func (store *store) Delete(ctx context.Context, orgID valuer.UUID, id valuer.UUID) error {
|
||||||
_, err := store.
|
_, err := store.
|
||||||
sqlstore.
|
sqlstore.
|
||||||
BunDB().
|
BunDBCtx(ctx).
|
||||||
NewDelete().
|
NewDelete().
|
||||||
Model(new(roletypes.StorableRole)).
|
Model(new(roletypes.StorableRole)).
|
||||||
Where("org_id = ?", orgID).
|
Where("org_id = ?", orgID).
|
||||||
|
|||||||
@@ -9,22 +9,16 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Module interface {
|
type Setter interface {
|
||||||
// Creates the role.
|
// Creates the role.
|
||||||
Create(context.Context, *roletypes.Role) error
|
Create(context.Context, valuer.UUID, *roletypes.Role) error
|
||||||
|
|
||||||
// Gets the role if it exists or creates one.
|
// Gets the role if it exists or creates one.
|
||||||
GetOrCreate(context.Context, *roletypes.Role) (*roletypes.Role, error)
|
GetOrCreate(context.Context, valuer.UUID, *roletypes.Role) (*roletypes.Role, error)
|
||||||
|
|
||||||
// Gets the role
|
|
||||||
Get(context.Context, valuer.UUID, valuer.UUID) (*roletypes.Role, error)
|
|
||||||
|
|
||||||
// Gets the objects associated with the given role and relation.
|
// Gets the objects associated with the given role and relation.
|
||||||
GetObjects(context.Context, valuer.UUID, valuer.UUID, authtypes.Relation) ([]*authtypes.Object, error)
|
GetObjects(context.Context, valuer.UUID, valuer.UUID, authtypes.Relation) ([]*authtypes.Object, error)
|
||||||
|
|
||||||
// Lists all the roles for the organization.
|
|
||||||
List(context.Context, valuer.UUID) ([]*roletypes.Role, error)
|
|
||||||
|
|
||||||
// Gets all the typeable resources registered from role registry.
|
// Gets all the typeable resources registered from role registry.
|
||||||
GetResources(context.Context) []*authtypes.Resource
|
GetResources(context.Context) []*authtypes.Resource
|
||||||
|
|
||||||
@@ -37,12 +31,40 @@ type Module interface {
|
|||||||
// Deletes the role and tuples in authorization server.
|
// Deletes the role and tuples in authorization server.
|
||||||
Delete(context.Context, valuer.UUID, valuer.UUID) error
|
Delete(context.Context, valuer.UUID, valuer.UUID) error
|
||||||
|
|
||||||
// Assigns role to the given subject.
|
|
||||||
Assign(context.Context, valuer.UUID, valuer.UUID, string) error
|
|
||||||
|
|
||||||
RegisterTypeable
|
RegisterTypeable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Getter interface {
|
||||||
|
// Gets the role
|
||||||
|
Get(context.Context, valuer.UUID, valuer.UUID) (*roletypes.Role, error)
|
||||||
|
|
||||||
|
// Gets the role by org_id and name
|
||||||
|
GetByOrgIDAndName(context.Context, valuer.UUID, string) (*roletypes.Role, error)
|
||||||
|
|
||||||
|
// Lists all the roles for the organization.
|
||||||
|
List(context.Context, valuer.UUID) ([]*roletypes.Role, error)
|
||||||
|
|
||||||
|
// Lists all the roles for the organization filtered by name
|
||||||
|
ListByOrgIDAndNames(context.Context, valuer.UUID, []string) ([]*roletypes.Role, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Granter interface {
|
||||||
|
// Grants a role to the subject based on role name.
|
||||||
|
Grant(context.Context, valuer.UUID, string, string) error
|
||||||
|
|
||||||
|
// Grants a role to the subject based on role id.
|
||||||
|
GrantByID(context.Context, valuer.UUID, valuer.UUID, string) error
|
||||||
|
|
||||||
|
// Revokes a granted role from the subject based on role name.
|
||||||
|
Revoke(context.Context, valuer.UUID, string, string) error
|
||||||
|
|
||||||
|
// Changes the granted role for the subject based on role name.
|
||||||
|
ModifyGrant(context.Context, valuer.UUID, string, string, string) error
|
||||||
|
|
||||||
|
// Bootstrap the managed roles.
|
||||||
|
CreateManagedRoles(context.Context, valuer.UUID, []*roletypes.Role) error
|
||||||
|
}
|
||||||
|
|
||||||
type RegisterTypeable interface {
|
type RegisterTypeable interface {
|
||||||
MustGetTypeables() []authtypes.Typeable
|
MustGetTypeables() []authtypes.Typeable
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/errors"
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
"github.com/SigNoz/signoz/pkg/factory"
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/user"
|
"github.com/SigNoz/signoz/pkg/modules/user"
|
||||||
root "github.com/SigNoz/signoz/pkg/modules/user"
|
root "github.com/SigNoz/signoz/pkg/modules/user"
|
||||||
"github.com/SigNoz/signoz/pkg/tokenizer"
|
"github.com/SigNoz/signoz/pkg/tokenizer"
|
||||||
@@ -29,12 +30,13 @@ type Module struct {
|
|||||||
emailing emailing.Emailing
|
emailing emailing.Emailing
|
||||||
settings factory.ScopedProviderSettings
|
settings factory.ScopedProviderSettings
|
||||||
orgSetter organization.Setter
|
orgSetter organization.Setter
|
||||||
|
granter role.Granter
|
||||||
analytics analytics.Analytics
|
analytics analytics.Analytics
|
||||||
config user.Config
|
config user.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// This module is a WIP, don't take inspiration from this.
|
// This module is a WIP, don't take inspiration from this.
|
||||||
func NewModule(store types.UserStore, tokenizer tokenizer.Tokenizer, emailing emailing.Emailing, providerSettings factory.ProviderSettings, orgSetter organization.Setter, analytics analytics.Analytics, config user.Config) root.Module {
|
func NewModule(store types.UserStore, tokenizer tokenizer.Tokenizer, emailing emailing.Emailing, providerSettings factory.ProviderSettings, orgSetter organization.Setter, granter role.Granter, analytics analytics.Analytics, config user.Config) root.Module {
|
||||||
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/modules/user/impluser")
|
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/modules/user/impluser")
|
||||||
return &Module{
|
return &Module{
|
||||||
store: store,
|
store: store,
|
||||||
@@ -43,6 +45,7 @@ func NewModule(store types.UserStore, tokenizer tokenizer.Tokenizer, emailing em
|
|||||||
settings: settings,
|
settings: settings,
|
||||||
orgSetter: orgSetter,
|
orgSetter: orgSetter,
|
||||||
analytics: analytics,
|
analytics: analytics,
|
||||||
|
granter: granter,
|
||||||
config: config,
|
config: config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,7 +230,6 @@ func (m *Module) UpdateUser(ctx context.Context, orgID valuer.UUID, id string, u
|
|||||||
}
|
}
|
||||||
|
|
||||||
user.UpdatedAt = time.Now()
|
user.UpdatedAt = time.Now()
|
||||||
|
|
||||||
updatedUser, err := m.store.UpdateUser(ctx, orgID, id, user)
|
updatedUser, err := m.store.UpdateUser(ctx, orgID, id, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -258,8 +260,8 @@ func (m *Module) UpdateUser(ctx context.Context, orgID valuer.UUID, id string, u
|
|||||||
return updatedUser, nil
|
return updatedUser, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) DeleteUser(ctx context.Context, orgID valuer.UUID, id string, deletedBy string) error {
|
func (module *Module) DeleteUser(ctx context.Context, orgID valuer.UUID, id string, deletedBy string) error {
|
||||||
user, err := m.store.GetUser(ctx, valuer.MustNewUUID(id))
|
user, err := module.store.GetUser(ctx, valuer.MustNewUUID(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -269,7 +271,7 @@ func (m *Module) DeleteUser(ctx context.Context, orgID valuer.UUID, id string, d
|
|||||||
}
|
}
|
||||||
|
|
||||||
// don't allow to delete the last admin user
|
// don't allow to delete the last admin user
|
||||||
adminUsers, err := m.store.GetUsersByRoleAndOrgID(ctx, types.RoleAdmin, orgID)
|
adminUsers, err := module.store.GetUsersByRoleAndOrgID(ctx, types.RoleAdmin, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -278,11 +280,11 @@ func (m *Module) DeleteUser(ctx context.Context, orgID valuer.UUID, id string, d
|
|||||||
return errors.New(errors.TypeForbidden, errors.CodeForbidden, "cannot delete the last admin")
|
return errors.New(errors.TypeForbidden, errors.CodeForbidden, "cannot delete the last admin")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := m.store.DeleteUser(ctx, orgID.String(), user.ID.StringValue()); err != nil {
|
if err := module.store.DeleteUser(ctx, orgID.String(), user.ID.StringValue()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.analytics.TrackUser(ctx, user.OrgID.String(), user.ID.String(), "User Deleted", map[string]any{
|
module.analytics.TrackUser(ctx, user.OrgID.String(), user.ID.String(), "User Deleted", map[string]any{
|
||||||
"deleted_by": deletedBy,
|
"deleted_by": deletedBy,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -476,7 +478,7 @@ func (module *Module) CreateFirstUser(ctx context.Context, organization *types.O
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err = module.store.RunInTx(ctx, func(ctx context.Context) error {
|
if err = module.store.RunInTx(ctx, func(ctx context.Context) error {
|
||||||
err := module.orgSetter.Create(ctx, organization)
|
err = module.orgSetter.Create(ctx, organization)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ func (s *Server) createPublicServer(api *APIHandler, web web.Web) (*http.Server,
|
|||||||
r.Use(middleware.NewLogging(s.signoz.Instrumentation.Logger(), s.config.APIServer.Logging.ExcludedRoutes).Wrap)
|
r.Use(middleware.NewLogging(s.signoz.Instrumentation.Logger(), s.config.APIServer.Logging.ExcludedRoutes).Wrap)
|
||||||
r.Use(middleware.NewComment().Wrap)
|
r.Use(middleware.NewComment().Wrap)
|
||||||
|
|
||||||
am := middleware.NewAuthZ(s.signoz.Instrumentation.Logger(), s.signoz.Modules.OrgGetter, s.signoz.Authz)
|
am := middleware.NewAuthZ(s.signoz.Instrumentation.Logger(), s.signoz.Modules.OrgGetter, s.signoz.Authz, s.signoz.Modules.RoleGetter)
|
||||||
|
|
||||||
api.RegisterRoutes(r, am)
|
api.RegisterRoutes(r, am)
|
||||||
api.RegisterLogsRoutes(r, am)
|
api.RegisterLogsRoutes(r, am)
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/modules/quickfilter/implquickfilter"
|
"github.com/SigNoz/signoz/pkg/modules/quickfilter/implquickfilter"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/rawdataexport"
|
"github.com/SigNoz/signoz/pkg/modules/rawdataexport"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/rawdataexport/implrawdataexport"
|
"github.com/SigNoz/signoz/pkg/modules/rawdataexport/implrawdataexport"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role/implrole"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
|
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/services"
|
"github.com/SigNoz/signoz/pkg/modules/services"
|
||||||
@@ -41,6 +43,7 @@ type Handlers struct {
|
|||||||
Global global.Handler
|
Global global.Handler
|
||||||
FlaggerHandler flagger.Handler
|
FlaggerHandler flagger.Handler
|
||||||
GatewayHandler gateway.Handler
|
GatewayHandler gateway.Handler
|
||||||
|
Role role.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandlers(modules Modules, providerSettings factory.ProviderSettings, querier querier.Querier, licensing licensing.Licensing, global global.Global, flaggerService flagger.Flagger, gatewayService gateway.Gateway) Handlers {
|
func NewHandlers(modules Modules, providerSettings factory.ProviderSettings, querier querier.Querier, licensing licensing.Licensing, global global.Global, flaggerService flagger.Flagger, gatewayService gateway.Gateway) Handlers {
|
||||||
@@ -57,5 +60,6 @@ func NewHandlers(modules Modules, providerSettings factory.ProviderSettings, que
|
|||||||
Global: signozglobal.NewHandler(global),
|
Global: signozglobal.NewHandler(global),
|
||||||
FlaggerHandler: flagger.NewHandler(flaggerService),
|
FlaggerHandler: flagger.NewHandler(flaggerService),
|
||||||
GatewayHandler: gateway.NewHandler(gatewayService),
|
GatewayHandler: gateway.NewHandler(gatewayService),
|
||||||
|
Role: implrole.NewHandler(modules.RoleSetter, modules.RoleGetter),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/factory/factorytest"
|
"github.com/SigNoz/signoz/pkg/factory/factorytest"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
|
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role/implrole"
|
||||||
"github.com/SigNoz/signoz/pkg/queryparser"
|
"github.com/SigNoz/signoz/pkg/queryparser"
|
||||||
"github.com/SigNoz/signoz/pkg/sharder"
|
"github.com/SigNoz/signoz/pkg/sharder"
|
||||||
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
|
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
|
||||||
@@ -40,7 +41,10 @@ func TestNewHandlers(t *testing.T) {
|
|||||||
queryParser := queryparser.New(providerSettings)
|
queryParser := queryparser.New(providerSettings)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
dashboardModule := impldashboard.NewModule(impldashboard.NewStore(sqlstore), providerSettings, nil, orgGetter, queryParser)
|
dashboardModule := impldashboard.NewModule(impldashboard.NewStore(sqlstore), providerSettings, nil, orgGetter, queryParser)
|
||||||
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, nil, nil, nil, nil, nil, nil, nil, queryParser, Config{}, dashboardModule)
|
roleSetter := implrole.NewSetter(implrole.NewStore(sqlstore), nil)
|
||||||
|
roleGetter := implrole.NewGetter(implrole.NewStore(sqlstore))
|
||||||
|
grantModule := implrole.NewGranter(implrole.NewStore(sqlstore), nil)
|
||||||
|
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, nil, nil, nil, nil, nil, nil, nil, queryParser, Config{}, dashboardModule, roleSetter, roleGetter, grantModule)
|
||||||
|
|
||||||
handlers := NewHandlers(modules, providerSettings, nil, nil, nil, nil, nil)
|
handlers := NewHandlers(modules, providerSettings, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/modules/quickfilter/implquickfilter"
|
"github.com/SigNoz/signoz/pkg/modules/quickfilter/implquickfilter"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/rawdataexport"
|
"github.com/SigNoz/signoz/pkg/modules/rawdataexport"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/rawdataexport/implrawdataexport"
|
"github.com/SigNoz/signoz/pkg/modules/rawdataexport/implrawdataexport"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
|
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/services"
|
"github.com/SigNoz/signoz/pkg/modules/services"
|
||||||
@@ -66,6 +67,9 @@ type Modules struct {
|
|||||||
SpanPercentile spanpercentile.Module
|
SpanPercentile spanpercentile.Module
|
||||||
MetricsExplorer metricsexplorer.Module
|
MetricsExplorer metricsexplorer.Module
|
||||||
Promote promote.Module
|
Promote promote.Module
|
||||||
|
RoleSetter role.Setter
|
||||||
|
RoleGetter role.Getter
|
||||||
|
Granter role.Granter
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModules(
|
func NewModules(
|
||||||
@@ -85,10 +89,13 @@ func NewModules(
|
|||||||
queryParser queryparser.QueryParser,
|
queryParser queryparser.QueryParser,
|
||||||
config Config,
|
config Config,
|
||||||
dashboard dashboard.Module,
|
dashboard dashboard.Module,
|
||||||
|
roleSetter role.Setter,
|
||||||
|
roleGetter role.Getter,
|
||||||
|
granter role.Granter,
|
||||||
) Modules {
|
) Modules {
|
||||||
quickfilter := implquickfilter.NewModule(implquickfilter.NewStore(sqlstore))
|
quickfilter := implquickfilter.NewModule(implquickfilter.NewStore(sqlstore))
|
||||||
orgSetter := implorganization.NewSetter(implorganization.NewStore(sqlstore), alertmanager, quickfilter)
|
orgSetter := implorganization.NewSetter(implorganization.NewStore(sqlstore), alertmanager, quickfilter)
|
||||||
user := impluser.NewModule(impluser.NewStore(sqlstore, providerSettings), tokenizer, emailing, providerSettings, orgSetter, analytics, config.User)
|
user := impluser.NewModule(impluser.NewStore(sqlstore, providerSettings), tokenizer, emailing, providerSettings, orgSetter, granter, analytics, config.User)
|
||||||
userGetter := impluser.NewGetter(impluser.NewStore(sqlstore, providerSettings))
|
userGetter := impluser.NewGetter(impluser.NewStore(sqlstore, providerSettings))
|
||||||
ruleStore := sqlrulestore.NewRuleStore(sqlstore, queryParser, providerSettings)
|
ruleStore := sqlrulestore.NewRuleStore(sqlstore, queryParser, providerSettings)
|
||||||
|
|
||||||
@@ -110,5 +117,8 @@ func NewModules(
|
|||||||
Services: implservices.NewModule(querier, telemetryStore),
|
Services: implservices.NewModule(querier, telemetryStore),
|
||||||
MetricsExplorer: implmetricsexplorer.NewModule(telemetryStore, telemetryMetadataStore, cache, ruleStore, dashboard, providerSettings, config.MetricsExplorer),
|
MetricsExplorer: implmetricsexplorer.NewModule(telemetryStore, telemetryMetadataStore, cache, ruleStore, dashboard, providerSettings, config.MetricsExplorer),
|
||||||
Promote: implpromote.NewModule(telemetryMetadataStore, telemetryStore),
|
Promote: implpromote.NewModule(telemetryMetadataStore, telemetryStore),
|
||||||
|
RoleSetter: roleSetter,
|
||||||
|
RoleGetter: roleGetter,
|
||||||
|
Granter: granter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/factory/factorytest"
|
"github.com/SigNoz/signoz/pkg/factory/factorytest"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
|
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role/implrole"
|
||||||
"github.com/SigNoz/signoz/pkg/queryparser"
|
"github.com/SigNoz/signoz/pkg/queryparser"
|
||||||
"github.com/SigNoz/signoz/pkg/sharder"
|
"github.com/SigNoz/signoz/pkg/sharder"
|
||||||
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
|
"github.com/SigNoz/signoz/pkg/sharder/noopsharder"
|
||||||
@@ -40,7 +41,10 @@ func TestNewModules(t *testing.T) {
|
|||||||
queryParser := queryparser.New(providerSettings)
|
queryParser := queryparser.New(providerSettings)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
dashboardModule := impldashboard.NewModule(impldashboard.NewStore(sqlstore), providerSettings, nil, orgGetter, queryParser)
|
dashboardModule := impldashboard.NewModule(impldashboard.NewStore(sqlstore), providerSettings, nil, orgGetter, queryParser)
|
||||||
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, nil, nil, nil, nil, nil, nil, nil, queryParser, Config{}, dashboardModule)
|
roleSetter := implrole.NewSetter(implrole.NewStore(sqlstore), nil)
|
||||||
|
roleGetter := implrole.NewGetter(implrole.NewStore(sqlstore))
|
||||||
|
grantModule := implrole.NewGranter(implrole.NewStore(sqlstore), nil)
|
||||||
|
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, nil, nil, nil, nil, nil, nil, nil, queryParser, Config{}, dashboardModule, roleSetter, roleGetter, grantModule)
|
||||||
|
|
||||||
reflectVal := reflect.ValueOf(modules)
|
reflectVal := reflect.ValueOf(modules)
|
||||||
for i := 0; i < reflectVal.NumField(); i++ {
|
for i := 0; i < reflectVal.NumField(); i++ {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/preference"
|
"github.com/SigNoz/signoz/pkg/modules/preference"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/promote"
|
"github.com/SigNoz/signoz/pkg/modules/promote"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/role"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/session"
|
"github.com/SigNoz/signoz/pkg/modules/session"
|
||||||
"github.com/SigNoz/signoz/pkg/modules/user"
|
"github.com/SigNoz/signoz/pkg/modules/user"
|
||||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||||
@@ -49,6 +50,8 @@ func NewOpenAPI(ctx context.Context, instrumentation instrumentation.Instrumenta
|
|||||||
struct{ dashboard.Handler }{},
|
struct{ dashboard.Handler }{},
|
||||||
struct{ metricsexplorer.Handler }{},
|
struct{ metricsexplorer.Handler }{},
|
||||||
struct{ gateway.Handler }{},
|
struct{ gateway.Handler }{},
|
||||||
|
struct{ role.Getter }{},
|
||||||
|
struct{ role.Handler }{},
|
||||||
).New(ctx, instrumentation.ToProviderSettings(), apiserver.Config{})
|
).New(ctx, instrumentation.ToProviderSettings(), apiserver.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -243,6 +243,8 @@ func NewAPIServerProviderFactories(orgGetter organization.Getter, authz authz.Au
|
|||||||
handlers.Dashboard,
|
handlers.Dashboard,
|
||||||
handlers.MetricsExplorer,
|
handlers.MetricsExplorer,
|
||||||
handlers.GatewayHandler,
|
handlers.GatewayHandler,
|
||||||
|
modules.RoleGetter,
|
||||||
|
handlers.Role,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,8 +90,9 @@ func New(
|
|||||||
telemetrystoreProviderFactories factory.NamedMap[factory.ProviderFactory[telemetrystore.TelemetryStore, telemetrystore.Config]],
|
telemetrystoreProviderFactories factory.NamedMap[factory.ProviderFactory[telemetrystore.TelemetryStore, telemetrystore.Config]],
|
||||||
authNsCallback func(ctx context.Context, providerSettings factory.ProviderSettings, store authtypes.AuthNStore, licensing licensing.Licensing) (map[authtypes.AuthNProvider]authn.AuthN, error),
|
authNsCallback func(ctx context.Context, providerSettings factory.ProviderSettings, store authtypes.AuthNStore, licensing licensing.Licensing) (map[authtypes.AuthNProvider]authn.AuthN, error),
|
||||||
authzCallback func(context.Context, sqlstore.SQLStore) factory.ProviderFactory[authz.AuthZ, authz.Config],
|
authzCallback func(context.Context, sqlstore.SQLStore) factory.ProviderFactory[authz.AuthZ, authz.Config],
|
||||||
dashboardModuleCallback func(sqlstore.SQLStore, factory.ProviderSettings, analytics.Analytics, organization.Getter, role.Module, queryparser.QueryParser, querier.Querier, licensing.Licensing) dashboard.Module,
|
dashboardModuleCallback func(sqlstore.SQLStore, factory.ProviderSettings, analytics.Analytics, organization.Getter, role.Setter, role.Granter, queryparser.QueryParser, querier.Querier, licensing.Licensing) dashboard.Module,
|
||||||
gatewayProviderFactory func(licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config],
|
gatewayProviderFactory func(licensing.Licensing) factory.ProviderFactory[gateway.Gateway, gateway.Config],
|
||||||
|
roleSetterCallback func(sqlstore.SQLStore, authz.AuthZ, licensing.Licensing, []role.RegisterTypeable) role.Setter,
|
||||||
) (*SigNoz, error) {
|
) (*SigNoz, error) {
|
||||||
// Initialize instrumentation
|
// Initialize instrumentation
|
||||||
instrumentation, err := instrumentation.New(ctx, config.Instrumentation, version.Info, "signoz")
|
instrumentation, err := instrumentation.New(ctx, config.Instrumentation, version.Info, "signoz")
|
||||||
@@ -280,6 +281,12 @@ func New(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize user getter
|
||||||
|
userGetter := impluser.NewGetter(impluser.NewStore(sqlstore, providerSettings))
|
||||||
|
|
||||||
|
// Initialize the role getter
|
||||||
|
roleGetter := implrole.NewGetter(implrole.NewStore(sqlstore))
|
||||||
|
|
||||||
// Initialize authz
|
// Initialize authz
|
||||||
authzProviderFactory := authzCallback(ctx, sqlstore)
|
authzProviderFactory := authzCallback(ctx, sqlstore)
|
||||||
authz, err := authzProviderFactory.New(ctx, providerSettings, authz.Config{})
|
authz, err := authzProviderFactory.New(ctx, providerSettings, authz.Config{})
|
||||||
@@ -287,9 +294,6 @@ func New(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize user getter
|
|
||||||
userGetter := impluser.NewGetter(impluser.NewStore(sqlstore, providerSettings))
|
|
||||||
|
|
||||||
// Initialize notification manager from the available notification manager provider factories
|
// Initialize notification manager from the available notification manager provider factories
|
||||||
nfManager, err := factory.NewProviderFromNamedMap(
|
nfManager, err := factory.NewProviderFromNamedMap(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -386,9 +390,10 @@ func New(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize all modules
|
// Initialize all modules
|
||||||
roleModule := implrole.NewModule(implrole.NewStore(sqlstore), authz, nil)
|
roleSetter := roleSetterCallback(sqlstore, authz, licensing, nil)
|
||||||
dashboardModule := dashboardModuleCallback(sqlstore, providerSettings, analytics, orgGetter, roleModule, queryParser, querier, licensing)
|
granter := implrole.NewGranter(implrole.NewStore(sqlstore), authz)
|
||||||
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, analytics, querier, telemetrystore, telemetryMetadataStore, authNs, authz, cache, queryParser, config, dashboardModule)
|
dashboard := dashboardModuleCallback(sqlstore, providerSettings, analytics, orgGetter, roleSetter, granter, queryParser, querier, licensing)
|
||||||
|
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, analytics, querier, telemetrystore, telemetryMetadataStore, authNs, authz, cache, queryParser, config, dashboard, roleSetter, roleGetter, granter)
|
||||||
|
|
||||||
// Initialize all handlers for the modules
|
// Initialize all handlers for the modules
|
||||||
handlers := NewHandlers(modules, providerSettings, querier, licensing, global, flagger, gateway)
|
handlers := NewHandlers(modules, providerSettings, querier, licensing, global, flagger, gateway)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ var (
|
|||||||
typeRoleSelectorRegex = regexp.MustCompile(`^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$`)
|
typeRoleSelectorRegex = regexp.MustCompile(`^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$`)
|
||||||
typeAnonymousSelectorRegex = regexp.MustCompile(`^\*$`)
|
typeAnonymousSelectorRegex = regexp.MustCompile(`^\*$`)
|
||||||
typeOrganizationSelectorRegex = regexp.MustCompile(`^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$`)
|
typeOrganizationSelectorRegex = regexp.MustCompile(`^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$`)
|
||||||
typeMetaResourceSelectorRegex = regexp.MustCompile(`^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$`)
|
typeMetaResourceSelectorRegex = regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`)
|
||||||
// metaresources selectors are used to select either all or none
|
// metaresources selectors are used to select either all or none
|
||||||
typeMetaResourcesSelectorRegex = regexp.MustCompile(`^\*$`)
|
typeMetaResourcesSelectorRegex = regexp.MustCompile(`^\*$`)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ var (
|
|||||||
ErrCodeInvalidTypeRelation = errors.MustNewCode("role_invalid_type_relation")
|
ErrCodeInvalidTypeRelation = errors.MustNewCode("role_invalid_type_relation")
|
||||||
ErrCodeRoleNotFound = errors.MustNewCode("role_not_found")
|
ErrCodeRoleNotFound = errors.MustNewCode("role_not_found")
|
||||||
ErrCodeRoleFailedTransactionsFromString = errors.MustNewCode("role_failed_transactions_from_string")
|
ErrCodeRoleFailedTransactionsFromString = errors.MustNewCode("role_failed_transactions_from_string")
|
||||||
|
ErrCodeRoleUnsupported = errors.MustNewCode("role_unsupported")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -32,8 +33,22 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
AnonymousUserRoleName = "signoz-anonymous"
|
SigNozAnonymousRoleName = "signoz-anonymous"
|
||||||
AnonymousUserRoleDescription = "Role assigned to anonymous users for access to public resources."
|
SigNozAnonymousRoleDescription = "Role assigned to anonymous users for access to public resources."
|
||||||
|
SigNozAdminRoleName = "signoz-admin"
|
||||||
|
SigNozAdminRoleDescription = "Role assigned to users who have full administrative access to SigNoz resources."
|
||||||
|
SigNozEditorRoleName = "signoz-editor"
|
||||||
|
SigNozEditorRoleDescription = "Role assigned to users who can create, edit, and manage SigNoz resources but do not have full administrative privileges."
|
||||||
|
SigNozViewerRoleName = "signoz-viewer"
|
||||||
|
SigNozViewerRoleDescription = "Role assigned to users who have read-only access to SigNoz resources."
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ExistingRoleToSigNozManagedRoleMap = map[types.Role]string{
|
||||||
|
types.RoleAdmin: SigNozAdminRoleName,
|
||||||
|
types.RoleEditor: SigNozEditorRoleName,
|
||||||
|
types.RoleViewer: SigNozViewerRoleName,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -54,10 +69,10 @@ type StorableRole struct {
|
|||||||
type Role struct {
|
type Role struct {
|
||||||
types.Identifiable
|
types.Identifiable
|
||||||
types.TimeAuditable
|
types.TimeAuditable
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Type string `json:"type"`
|
Type valuer.String `json:"type"`
|
||||||
OrgID valuer.UUID `json:"org_id"`
|
OrgID valuer.UUID `json:"orgId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostableRole struct {
|
type PostableRole struct {
|
||||||
@@ -81,7 +96,7 @@ func NewStorableRoleFromRole(role *Role) *StorableRole {
|
|||||||
TimeAuditable: role.TimeAuditable,
|
TimeAuditable: role.TimeAuditable,
|
||||||
Name: role.Name,
|
Name: role.Name,
|
||||||
Description: role.Description,
|
Description: role.Description,
|
||||||
Type: role.Type,
|
Type: role.Type.String(),
|
||||||
OrgID: role.OrgID.StringValue(),
|
OrgID: role.OrgID.StringValue(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,12 +107,12 @@ func NewRoleFromStorableRole(storableRole *StorableRole) *Role {
|
|||||||
TimeAuditable: storableRole.TimeAuditable,
|
TimeAuditable: storableRole.TimeAuditable,
|
||||||
Name: storableRole.Name,
|
Name: storableRole.Name,
|
||||||
Description: storableRole.Description,
|
Description: storableRole.Description,
|
||||||
Type: storableRole.Type,
|
Type: valuer.NewString(storableRole.Type),
|
||||||
OrgID: valuer.MustNewUUID(storableRole.OrgID),
|
OrgID: valuer.MustNewUUID(storableRole.OrgID),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRole(name, description string, roleType string, orgID valuer.UUID) *Role {
|
func NewRole(name, description string, roleType valuer.String, orgID valuer.UUID) *Role {
|
||||||
return &Role{
|
return &Role{
|
||||||
Identifiable: types.Identifiable{
|
Identifiable: types.Identifiable{
|
||||||
ID: valuer.GenerateUUID(),
|
ID: valuer.GenerateUUID(),
|
||||||
@@ -113,7 +128,38 @@ func NewRole(name, description string, roleType string, orgID valuer.UUID) *Role
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPatchableObjects(additions []*authtypes.Object, deletions []*authtypes.Object, relation authtypes.Relation) (*PatchableObjects, error) {
|
func NewManagedRoles(orgID valuer.UUID) []*Role {
|
||||||
|
return []*Role{
|
||||||
|
NewRole(SigNozAdminRoleName, SigNozAdminRoleDescription, RoleTypeManaged, orgID),
|
||||||
|
NewRole(SigNozEditorRoleName, SigNozEditorRoleDescription, RoleTypeManaged, orgID),
|
||||||
|
NewRole(SigNozViewerRoleName, SigNozViewerRoleDescription, RoleTypeManaged, orgID),
|
||||||
|
NewRole(SigNozAnonymousRoleName, SigNozAnonymousRoleDescription, RoleTypeManaged, orgID),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (role *Role) PatchMetadata(name, description *string) error {
|
||||||
|
err := role.CanEditDelete()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != nil {
|
||||||
|
role.Name = *name
|
||||||
|
}
|
||||||
|
if description != nil {
|
||||||
|
role.Description = *description
|
||||||
|
}
|
||||||
|
role.UpdatedAt = time.Now()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (role *Role) NewPatchableObjects(additions []*authtypes.Object, deletions []*authtypes.Object, relation authtypes.Relation) (*PatchableObjects, error) {
|
||||||
|
err := role.CanEditDelete()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if len(additions) == 0 && len(deletions) == 0 {
|
if len(additions) == 0 && len(deletions) == 0 {
|
||||||
return nil, errors.New(errors.TypeInvalidInput, ErrCodeRoleEmptyPatch, "empty object patch request received, at least one of additions or deletions must be present")
|
return nil, errors.New(errors.TypeInvalidInput, ErrCodeRoleEmptyPatch, "empty object patch request received, at least one of additions or deletions must be present")
|
||||||
}
|
}
|
||||||
@@ -133,14 +179,12 @@ func NewPatchableObjects(additions []*authtypes.Object, deletions []*authtypes.O
|
|||||||
return &PatchableObjects{Additions: additions, Deletions: deletions}, nil
|
return &PatchableObjects{Additions: additions, Deletions: deletions}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (role *Role) PatchMetadata(name, description *string) {
|
func (role *Role) CanEditDelete() error {
|
||||||
if name != nil {
|
if role.Type == RoleTypeManaged {
|
||||||
role.Name = *name
|
return errors.Newf(errors.TypeInvalidInput, ErrCodeRoleInvalidInput, "cannot edit/delete managed role: %s", role.Name)
|
||||||
}
|
}
|
||||||
if description != nil {
|
|
||||||
role.Description = *description
|
return nil
|
||||||
}
|
|
||||||
role.UpdatedAt = time.Now()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (role *PostableRole) UnmarshalJSON(data []byte) error {
|
func (role *PostableRole) UnmarshalJSON(data []byte) error {
|
||||||
@@ -246,3 +290,12 @@ func GetDeletionTuples(id valuer.UUID, orgID valuer.UUID, relation authtypes.Rel
|
|||||||
|
|
||||||
return tuples, nil
|
return tuples, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MustGetSigNozManagedRoleFromExistingRole(role types.Role) string {
|
||||||
|
managedRole, ok := ExistingRoleToSigNozManagedRoleMap[role]
|
||||||
|
if !ok {
|
||||||
|
panic(errors.Newf(errors.TypeInternal, errors.CodeInternal, "invalid role: %s", role.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return managedRole
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ import (
|
|||||||
type Store interface {
|
type Store interface {
|
||||||
Create(context.Context, *StorableRole) error
|
Create(context.Context, *StorableRole) error
|
||||||
Get(context.Context, valuer.UUID, valuer.UUID) (*StorableRole, error)
|
Get(context.Context, valuer.UUID, valuer.UUID) (*StorableRole, error)
|
||||||
GetByNameAndOrgID(context.Context, string, valuer.UUID) (*StorableRole, error)
|
GetByOrgIDAndName(context.Context, valuer.UUID, string) (*StorableRole, error)
|
||||||
List(context.Context, valuer.UUID) ([]*StorableRole, error)
|
List(context.Context, valuer.UUID) ([]*StorableRole, error)
|
||||||
|
ListByOrgIDAndNames(context.Context, valuer.UUID, []string) ([]*StorableRole, error)
|
||||||
Update(context.Context, valuer.UUID, *StorableRole) error
|
Update(context.Context, valuer.UUID, *StorableRole) error
|
||||||
Delete(context.Context, valuer.UUID, valuer.UUID) error
|
Delete(context.Context, valuer.UUID, valuer.UUID) error
|
||||||
RunInTx(context.Context, func(ctx context.Context) error) error
|
RunInTx(context.Context, func(ctx context.Context) error) error
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ USER_ADMIN_NAME = "admin"
|
|||||||
USER_ADMIN_EMAIL = "admin@integration.test"
|
USER_ADMIN_EMAIL = "admin@integration.test"
|
||||||
USER_ADMIN_PASSWORD = "password123Z$"
|
USER_ADMIN_PASSWORD = "password123Z$"
|
||||||
|
|
||||||
|
USER_EDITOR_NAME = 'editor'
|
||||||
|
USER_EDITOR_EMAIL = 'editor@integration.test'
|
||||||
|
USER_EDITOR_PASSWORD = 'password123Z$'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="create_user_admin", scope="package")
|
@pytest.fixture(name="create_user_admin", scope="package")
|
||||||
def create_user_admin(
|
def create_user_admin(
|
||||||
|
|||||||
Reference in New Issue
Block a user