Compare commits

..

1 Commits

Author SHA1 Message Date
Karan Balani
1a5e60dcdd feat: control visibility of root user in list user api using flagger 2026-02-20 21:32:27 +05:30
37 changed files with 250 additions and 1362 deletions

View File

@@ -92,19 +92,6 @@ components:
tokenType:
type: string
type: object
AuthtypesGettableTransaction:
properties:
authorized:
type: boolean
object:
$ref: '#/components/schemas/AuthtypesObject'
relation:
type: string
required:
- relation
- object
- authorized
type: object
AuthtypesGoogleConfig:
properties:
allowedGroups:
@@ -130,8 +117,6 @@ components:
serviceAccountJson:
type: string
type: object
AuthtypesName:
type: object
AuthtypesOIDCConfig:
properties:
claimMapping:
@@ -149,16 +134,6 @@ components:
issuerAlias:
type: string
type: object
AuthtypesObject:
properties:
resource:
$ref: '#/components/schemas/AuthtypesResource'
selector:
$ref: '#/components/schemas/AuthtypesSelector'
required:
- resource
- selector
type: object
AuthtypesOrgSessionContext:
properties:
authNSupport:
@@ -196,16 +171,6 @@ components:
refreshToken:
type: string
type: object
AuthtypesResource:
properties:
name:
$ref: '#/components/schemas/AuthtypesName'
type:
type: string
required:
- name
- type
type: object
AuthtypesRoleMapping:
properties:
defaultRole:
@@ -231,8 +196,6 @@ components:
samlIdp:
type: string
type: object
AuthtypesSelector:
type: object
AuthtypesSessionContext:
properties:
exists:
@@ -243,18 +206,6 @@ components:
nullable: true
type: array
type: object
AuthtypesTransaction:
properties:
id:
type: string
object:
$ref: '#/components/schemas/AuthtypesObject'
relation:
type: string
required:
- relation
- object
type: object
AuthtypesUpdateableAuthDomain:
properties:
config:
@@ -1662,56 +1613,6 @@ components:
status:
type: string
type: object
RoletypesGettableResources:
properties:
relations:
additionalProperties:
items:
type: string
type: array
nullable: true
type: object
resources:
items:
$ref: '#/components/schemas/AuthtypesResource'
nullable: true
type: array
required:
- resources
- relations
type: object
RoletypesPatchableObjects:
properties:
additions:
items:
$ref: '#/components/schemas/AuthtypesObject'
nullable: true
type: array
deletions:
items:
$ref: '#/components/schemas/AuthtypesObject'
nullable: true
type: array
required:
- additions
- deletions
type: object
RoletypesPatchableRole:
properties:
description:
type: string
required:
- description
type: object
RoletypesPostableRole:
properties:
description:
type: string
name:
type: string
required:
- name
type: object
RoletypesRole:
properties:
createdAt:
@@ -1730,11 +1631,6 @@ components:
updatedAt:
format: date-time
type: string
required:
- name
- description
- type
- orgId
type: object
TelemetrytypesFieldContext:
enum:
@@ -2126,41 +2022,6 @@ info:
version: ""
openapi: 3.0.3
paths:
/api/v1/authz/check:
post:
deprecated: false
description: Checks if the authenticated user has permissions for given transactions
operationId: AuthzCheck
requestBody:
content:
application/json:
schema:
items:
$ref: '#/components/schemas/AuthtypesTransaction'
type: array
responses:
"200":
content:
application/json:
schema:
properties:
data:
items:
$ref: '#/components/schemas/AuthtypesGettableTransaction'
type: array
status:
type: string
type: object
description: OK
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
summary: Check permissions
tags:
- authz
/api/v1/changePassword/{id}:
post:
deprecated: false
@@ -3990,11 +3851,6 @@ paths:
deprecated: false
description: This endpoint creates a role
operationId: CreateRole
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/RoletypesPostableRole'
responses:
"201":
content:
@@ -4007,12 +3863,6 @@ paths:
type: string
type: object
description: Created
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
@@ -4025,30 +3875,12 @@ paths:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"409":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Conflict
"451":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unavailable For Legal Reasons
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
"501":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Implemented
security:
- api_key:
- ADMIN
@@ -4087,30 +3919,12 @@ paths:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"451":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unavailable For Legal Reasons
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
"501":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Implemented
security:
- api_key:
- ADMIN
@@ -4177,11 +3991,6 @@ paths:
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/RoletypesPatchableRole'
responses:
"204":
content:
@@ -4201,30 +4010,12 @@ paths:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"451":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unavailable For Legal Reasons
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
"501":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Implemented
security:
- api_key:
- ADMIN
@@ -4233,202 +4024,6 @@ paths:
summary: Patch role
tags:
- role
/api/v1/roles/{id}/relation/{relation}/objects:
get:
deprecated: false
description: Gets all objects connected to the specified role via a given relation
type
operationId: GetObjects
parameters:
- in: path
name: id
required: true
schema:
type: string
- in: path
name: relation
required: true
schema:
type: string
responses:
"200":
content:
application/json:
schema:
properties:
data:
items:
$ref: '#/components/schemas/AuthtypesObject'
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
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"451":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unavailable For Legal Reasons
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
"501":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Implemented
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Get objects for a role by relation
tags:
- role
patch:
deprecated: false
description: Patches the objects connected to the specified role via a given
relation type
operationId: PatchObjects
parameters:
- in: path
name: id
required: true
schema:
type: string
- in: path
name: relation
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/RoletypesPatchableObjects'
responses:
"204":
content:
application/json:
schema:
type: string
description: No Content
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"451":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unavailable For Legal Reasons
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
"501":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Implemented
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Patch objects for a role by relation
tags:
- role
/api/v1/roles/resources:
get:
deprecated: false
description: Gets all the available resources for role assignment
operationId: GetResources
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/RoletypesGettableResources'
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 resources
tags:
- role
/api/v1/user:
get:
deprecated: false

View File

@@ -62,6 +62,10 @@ func (provider *provider) Stop(ctx context.Context) error {
return provider.openfgaServer.Stop(ctx)
}
func (provider *provider) Check(ctx context.Context, tuple *openfgav1.TupleKey) error {
return provider.openfgaServer.Check(ctx, tuple)
}
func (provider *provider) CheckWithTupleCreation(ctx context.Context, claims authtypes.Claims, orgID valuer.UUID, relation authtypes.Relation, typeable authtypes.Typeable, selectors []authtypes.Selector, roleSelectors []authtypes.Selector) error {
return provider.openfgaServer.CheckWithTupleCreation(ctx, claims, orgID, relation, typeable, selectors, roleSelectors)
}
@@ -70,8 +74,8 @@ func (provider *provider) CheckWithTupleCreationWithoutClaims(ctx context.Contex
return provider.openfgaServer.CheckWithTupleCreationWithoutClaims(ctx, orgID, relation, typeable, selectors, roleSelectors)
}
func (provider *provider) BatchCheck(ctx context.Context, tupleReq map[string]*openfgav1.TupleKey) (map[string]*authtypes.TupleKeyAuthorization, error) {
return provider.openfgaServer.BatchCheck(ctx, tupleReq)
func (provider *provider) BatchCheck(ctx context.Context, tuples []*openfgav1.TupleKey) error {
return provider.openfgaServer.BatchCheck(ctx, tuples)
}
func (provider *provider) ListObjects(ctx context.Context, subject string, relation authtypes.Relation, typeable authtypes.Typeable) ([]*authtypes.Object, error) {
@@ -183,11 +187,6 @@ func (provider *provider) GetResources(_ context.Context) []*authtypes.Resource
}
func (provider *provider) GetObjects(ctx context.Context, orgID valuer.UUID, id valuer.UUID, relation authtypes.Relation) ([]*authtypes.Object, error) {
_, err := provider.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())
}
storableRole, err := provider.store.Get(ctx, orgID, id)
if err != nil {
return nil, err
@@ -199,7 +198,7 @@ func (provider *provider) GetObjects(ctx context.Context, orgID valuer.UUID, id
resourceObjects, err := provider.
ListObjects(
ctx,
authtypes.MustNewSubject(authtypes.TypeableRole, storableRole.Name, orgID, &authtypes.RelationAssignee),
authtypes.MustNewSubject(authtypes.TypeableRole, storableRole.ID.String(), orgID, &authtypes.RelationAssignee),
relation,
authtypes.MustNewTypeableFromType(resource.Type, resource.Name),
)

View File

@@ -2,10 +2,8 @@ package openfgaserver
import (
"context"
"strconv"
"github.com/SigNoz/signoz/pkg/authz"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/valuer"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
@@ -30,34 +28,27 @@ func (server *Server) Stop(ctx context.Context) error {
return server.pkgAuthzService.Stop(ctx)
}
func (server *Server) Check(ctx context.Context, tuple *openfgav1.TupleKey) error {
return server.pkgAuthzService.Check(ctx, tuple)
}
func (server *Server) 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)
if err != nil {
return err
}
tupleSlice, err := typeable.Tuples(subject, relation, selectors, orgID)
tuples, err := typeable.Tuples(subject, relation, selectors, orgID)
if err != nil {
return err
}
tuples := make(map[string]*openfgav1.TupleKey, len(tupleSlice))
for idx, tuple := range tupleSlice {
tuples[strconv.Itoa(idx)] = tuple
}
response, err := server.BatchCheck(ctx, tuples)
err = server.BatchCheck(ctx, tuples)
if err != nil {
return err
}
for _, resp := range response {
if resp.Authorized {
return nil
}
}
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "subjects are not authorized for requested access")
return nil
}
func (server *Server) CheckWithTupleCreationWithoutClaims(ctx context.Context, orgID valuer.UUID, relation authtypes.Relation, typeable authtypes.Typeable, selectors []authtypes.Selector, _ []authtypes.Selector) error {
@@ -66,32 +57,21 @@ func (server *Server) CheckWithTupleCreationWithoutClaims(ctx context.Context, o
return err
}
tupleSlice, err := typeable.Tuples(subject, relation, selectors, orgID)
tuples, err := typeable.Tuples(subject, relation, selectors, orgID)
if err != nil {
return err
}
tuples := make(map[string]*openfgav1.TupleKey, len(tupleSlice))
for idx, tuple := range tupleSlice {
tuples[strconv.Itoa(idx)] = tuple
}
response, err := server.BatchCheck(ctx, tuples)
err = server.BatchCheck(ctx, tuples)
if err != nil {
return err
}
for _, resp := range response {
if resp.Authorized {
return nil
}
}
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "subjects are not authorized for requested access")
return nil
}
func (server *Server) BatchCheck(ctx context.Context, tupleReq map[string]*openfgav1.TupleKey) (map[string]*authtypes.TupleKeyAuthorization, error) {
return server.pkgAuthzService.BatchCheck(ctx, tupleReq)
func (server *Server) BatchCheck(ctx context.Context, tuples []*openfgav1.TupleKey) error {
return server.pkgAuthzService.BatchCheck(ctx, tuples)
}
func (server *Server) ListObjects(ctx context.Context, subject string, relation authtypes.Relation, typeable authtypes.Typeable) ([]*authtypes.Object, error) {

View File

@@ -220,7 +220,6 @@ func (module *module) MustGetManagedRoleTransactions() map[string][]*authtypes.T
return map[string][]*authtypes.Transaction{
roletypes.SigNozAnonymousRoleName: {
{
ID: valuer.GenerateUUID(),
Relation: authtypes.RelationRead,
Object: *authtypes.MustNewObject(
authtypes.Resource{

View File

@@ -1,107 +0,0 @@
/**
* ! Do not edit manually
* * The file has been auto-generated using Orval for SigNoz
* * regenerate with 'yarn generate:api'
* SigNoz
*/
import type {
MutationFunction,
UseMutationOptions,
UseMutationResult,
} from 'react-query';
import { useMutation } from 'react-query';
import { GeneratedAPIInstance } from '../../../index';
import type {
AuthtypesTransactionDTO,
AuthzCheck200,
RenderErrorResponseDTO,
} from '../sigNoz.schemas';
type AwaitedInput<T> = PromiseLike<T> | T;
type Awaited<O> = O extends AwaitedInput<infer T> ? T : never;
/**
* Checks if the authenticated user has permissions for given transactions
* @summary Check permissions
*/
export const authzCheck = (
authtypesTransactionDTO: AuthtypesTransactionDTO[],
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<AuthzCheck200>({
url: `/api/v1/authz/check`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: authtypesTransactionDTO,
signal,
});
};
export const getAuthzCheckMutationOptions = <
TError = RenderErrorResponseDTO,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof authzCheck>>,
TError,
{ data: AuthtypesTransactionDTO[] },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof authzCheck>>,
TError,
{ data: AuthtypesTransactionDTO[] },
TContext
> => {
const mutationKey = ['authzCheck'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof authzCheck>>,
{ data: AuthtypesTransactionDTO[] }
> = (props) => {
const { data } = props ?? {};
return authzCheck(data);
};
return { mutationFn, ...mutationOptions };
};
export type AuthzCheckMutationResult = NonNullable<
Awaited<ReturnType<typeof authzCheck>>
>;
export type AuthzCheckMutationBody = AuthtypesTransactionDTO[];
export type AuthzCheckMutationError = RenderErrorResponseDTO;
/**
* @summary Check permissions
*/
export const useAuthzCheck = <
TError = RenderErrorResponseDTO,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof authzCheck>>,
TError,
{ data: AuthtypesTransactionDTO[] },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof authzCheck>>,
TError,
{ data: AuthtypesTransactionDTO[] },
TContext
> => {
const mutationOptions = getAuthzCheckMutationOptions(options);
return useMutation(mutationOptions);
};

View File

@@ -21,18 +21,11 @@ import { GeneratedAPIInstance } from '../../../index';
import type {
CreateRole201,
DeleteRolePathParameters,
GetObjects200,
GetObjectsPathParameters,
GetResources200,
GetRole200,
GetRolePathParameters,
ListRoles200,
PatchObjectsPathParameters,
PatchRolePathParameters,
RenderErrorResponseDTO,
RoletypesPatchableObjectsDTO,
RoletypesPatchableRoleDTO,
RoletypesPostableRoleDTO,
} from '../sigNoz.schemas';
type AwaitedInput<T> = PromiseLike<T> | T;
@@ -121,15 +114,10 @@ export const invalidateListRoles = async (
* This endpoint creates a role
* @summary Create role
*/
export const createRole = (
roletypesPostableRoleDTO: RoletypesPostableRoleDTO,
signal?: AbortSignal,
) => {
export const createRole = (signal?: AbortSignal) => {
return GeneratedAPIInstance<CreateRole201>({
url: `/api/v1/roles`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: roletypesPostableRoleDTO,
signal,
});
};
@@ -141,13 +129,13 @@ export const getCreateRoleMutationOptions = <
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createRole>>,
TError,
{ data: RoletypesPostableRoleDTO },
void,
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof createRole>>,
TError,
{ data: RoletypesPostableRoleDTO },
void,
TContext
> => {
const mutationKey = ['createRole'];
@@ -161,11 +149,9 @@ export const getCreateRoleMutationOptions = <
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof createRole>>,
{ data: RoletypesPostableRoleDTO }
> = (props) => {
const { data } = props ?? {};
return createRole(data);
void
> = () => {
return createRole();
};
return { mutationFn, ...mutationOptions };
@@ -174,7 +160,7 @@ export const getCreateRoleMutationOptions = <
export type CreateRoleMutationResult = NonNullable<
Awaited<ReturnType<typeof createRole>>
>;
export type CreateRoleMutationBody = RoletypesPostableRoleDTO;
export type CreateRoleMutationError = RenderErrorResponseDTO;
/**
@@ -187,13 +173,13 @@ export const useCreateRole = <
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createRole>>,
TError,
{ data: RoletypesPostableRoleDTO },
void,
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof createRole>>,
TError,
{ data: RoletypesPostableRoleDTO },
void,
TContext
> => {
const mutationOptions = getCreateRoleMutationOptions(options);
@@ -372,15 +358,10 @@ export const invalidateGetRole = async (
* This endpoint patches a role
* @summary Patch role
*/
export const patchRole = (
{ id }: PatchRolePathParameters,
roletypesPatchableRoleDTO: RoletypesPatchableRoleDTO,
) => {
export const patchRole = ({ id }: PatchRolePathParameters) => {
return GeneratedAPIInstance<string>({
url: `/api/v1/roles/${id}`,
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
data: roletypesPatchableRoleDTO,
});
};
@@ -391,13 +372,13 @@ export const getPatchRoleMutationOptions = <
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof patchRole>>,
TError,
{ pathParams: PatchRolePathParameters; data: RoletypesPatchableRoleDTO },
{ pathParams: PatchRolePathParameters },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof patchRole>>,
TError,
{ pathParams: PatchRolePathParameters; data: RoletypesPatchableRoleDTO },
{ pathParams: PatchRolePathParameters },
TContext
> => {
const mutationKey = ['patchRole'];
@@ -411,11 +392,11 @@ export const getPatchRoleMutationOptions = <
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof patchRole>>,
{ pathParams: PatchRolePathParameters; data: RoletypesPatchableRoleDTO }
{ pathParams: PatchRolePathParameters }
> = (props) => {
const { pathParams, data } = props ?? {};
const { pathParams } = props ?? {};
return patchRole(pathParams, data);
return patchRole(pathParams);
};
return { mutationFn, ...mutationOptions };
@@ -424,7 +405,7 @@ export const getPatchRoleMutationOptions = <
export type PatchRoleMutationResult = NonNullable<
Awaited<ReturnType<typeof patchRole>>
>;
export type PatchRoleMutationBody = RoletypesPatchableRoleDTO;
export type PatchRoleMutationError = RenderErrorResponseDTO;
/**
@@ -437,292 +418,16 @@ export const usePatchRole = <
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof patchRole>>,
TError,
{ pathParams: PatchRolePathParameters; data: RoletypesPatchableRoleDTO },
{ pathParams: PatchRolePathParameters },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof patchRole>>,
TError,
{ pathParams: PatchRolePathParameters; data: RoletypesPatchableRoleDTO },
{ pathParams: PatchRolePathParameters },
TContext
> => {
const mutationOptions = getPatchRoleMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Gets all objects connected to the specified role via a given relation type
* @summary Get objects for a role by relation
*/
export const getObjects = (
{ id, relation }: GetObjectsPathParameters,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<GetObjects200>({
url: `/api/v1/roles/${id}/relation/${relation}/objects`,
method: 'GET',
signal,
});
};
export const getGetObjectsQueryKey = ({
id,
relation,
}: GetObjectsPathParameters) => {
return ['getObjects'] as const;
};
export const getGetObjectsQueryOptions = <
TData = Awaited<ReturnType<typeof getObjects>>,
TError = RenderErrorResponseDTO
>(
{ id, relation }: GetObjectsPathParameters,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getObjects>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getGetObjectsQueryKey({ id, relation });
const queryFn: QueryFunction<Awaited<ReturnType<typeof getObjects>>> = ({
signal,
}) => getObjects({ id, relation }, signal);
return {
queryKey,
queryFn,
enabled: !!(id && relation),
...queryOptions,
} as UseQueryOptions<Awaited<ReturnType<typeof getObjects>>, TError, TData> & {
queryKey: QueryKey;
};
};
export type GetObjectsQueryResult = NonNullable<
Awaited<ReturnType<typeof getObjects>>
>;
export type GetObjectsQueryError = RenderErrorResponseDTO;
/**
* @summary Get objects for a role by relation
*/
export function useGetObjects<
TData = Awaited<ReturnType<typeof getObjects>>,
TError = RenderErrorResponseDTO
>(
{ id, relation }: GetObjectsPathParameters,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getObjects>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetObjectsQueryOptions({ id, relation }, options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Get objects for a role by relation
*/
export const invalidateGetObjects = async (
queryClient: QueryClient,
{ id, relation }: GetObjectsPathParameters,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetObjectsQueryKey({ id, relation }) },
options,
);
return queryClient;
};
/**
* Patches the objects connected to the specified role via a given relation type
* @summary Patch objects for a role by relation
*/
export const patchObjects = (
{ id, relation }: PatchObjectsPathParameters,
roletypesPatchableObjectsDTO: RoletypesPatchableObjectsDTO,
) => {
return GeneratedAPIInstance<string>({
url: `/api/v1/roles/${id}/relation/${relation}/objects`,
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
data: roletypesPatchableObjectsDTO,
});
};
export const getPatchObjectsMutationOptions = <
TError = RenderErrorResponseDTO,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof patchObjects>>,
TError,
{
pathParams: PatchObjectsPathParameters;
data: RoletypesPatchableObjectsDTO;
},
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof patchObjects>>,
TError,
{ pathParams: PatchObjectsPathParameters; data: RoletypesPatchableObjectsDTO },
TContext
> => {
const mutationKey = ['patchObjects'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof patchObjects>>,
{ pathParams: PatchObjectsPathParameters; data: RoletypesPatchableObjectsDTO }
> = (props) => {
const { pathParams, data } = props ?? {};
return patchObjects(pathParams, data);
};
return { mutationFn, ...mutationOptions };
};
export type PatchObjectsMutationResult = NonNullable<
Awaited<ReturnType<typeof patchObjects>>
>;
export type PatchObjectsMutationBody = RoletypesPatchableObjectsDTO;
export type PatchObjectsMutationError = RenderErrorResponseDTO;
/**
* @summary Patch objects for a role by relation
*/
export const usePatchObjects = <
TError = RenderErrorResponseDTO,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof patchObjects>>,
TError,
{
pathParams: PatchObjectsPathParameters;
data: RoletypesPatchableObjectsDTO;
},
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof patchObjects>>,
TError,
{ pathParams: PatchObjectsPathParameters; data: RoletypesPatchableObjectsDTO },
TContext
> => {
const mutationOptions = getPatchObjectsMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Gets all the available resources for role assignment
* @summary Get resources
*/
export const getResources = (signal?: AbortSignal) => {
return GeneratedAPIInstance<GetResources200>({
url: `/api/v1/roles/resources`,
method: 'GET',
signal,
});
};
export const getGetResourcesQueryKey = () => {
return ['getResources'] as const;
};
export const getGetResourcesQueryOptions = <
TData = Awaited<ReturnType<typeof getResources>>,
TError = RenderErrorResponseDTO
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getResources>>,
TError,
TData
>;
}) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getGetResourcesQueryKey();
const queryFn: QueryFunction<Awaited<ReturnType<typeof getResources>>> = ({
signal,
}) => getResources(signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof getResources>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetResourcesQueryResult = NonNullable<
Awaited<ReturnType<typeof getResources>>
>;
export type GetResourcesQueryError = RenderErrorResponseDTO;
/**
* @summary Get resources
*/
export function useGetResources<
TData = Awaited<ReturnType<typeof getResources>>,
TError = RenderErrorResponseDTO
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getResources>>,
TError,
TData
>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetResourcesQueryOptions(options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Get resources
*/
export const invalidateGetResources = async (
queryClient: QueryClient,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetResourcesQueryKey() },
options,
);
return queryClient;
};

View File

@@ -127,18 +127,6 @@ export interface AuthtypesGettableTokenDTO {
tokenType?: string;
}
export interface AuthtypesGettableTransactionDTO {
/**
* @type boolean
*/
authorized: boolean;
object: AuthtypesObjectDTO;
/**
* @type string
*/
relation: string;
}
export type AuthtypesGoogleConfigDTODomainToAdminEmail = {
[key: string]: string;
};
@@ -182,10 +170,6 @@ export interface AuthtypesGoogleConfigDTO {
serviceAccountJson?: string;
}
export interface AuthtypesNameDTO {
[key: string]: unknown;
}
export interface AuthtypesOIDCConfigDTO {
claimMapping?: AuthtypesAttributeMappingDTO;
/**
@@ -214,11 +198,6 @@ export interface AuthtypesOIDCConfigDTO {
issuerAlias?: string;
}
export interface AuthtypesObjectDTO {
resource: AuthtypesResourceDTO;
selector: AuthtypesSelectorDTO;
}
export interface AuthtypesOrgSessionContextDTO {
authNSupport?: AuthtypesAuthNSupportDTO;
/**
@@ -269,14 +248,6 @@ export interface AuthtypesPostableRotateTokenDTO {
refreshToken?: string;
}
export interface AuthtypesResourceDTO {
name: AuthtypesNameDTO;
/**
* @type string
*/
type: string;
}
/**
* @nullable
*/
@@ -320,10 +291,6 @@ export interface AuthtypesSamlConfigDTO {
samlIdp?: string;
}
export interface AuthtypesSelectorDTO {
[key: string]: unknown;
}
export interface AuthtypesSessionContextDTO {
/**
* @type boolean
@@ -336,18 +303,6 @@ export interface AuthtypesSessionContextDTO {
orgs?: AuthtypesOrgSessionContextDTO[] | null;
}
export interface AuthtypesTransactionDTO {
/**
* @type string
*/
id?: string;
object: AuthtypesObjectDTO;
/**
* @type string
*/
relation: string;
}
export interface AuthtypesUpdateableAuthDomainDTO {
config?: AuthtypesAuthDomainConfigDTO;
}
@@ -1992,57 +1947,6 @@ export interface RenderErrorResponseDTO {
status?: string;
}
/**
* @nullable
*/
export type RoletypesGettableResourcesDTORelations = {
[key: string]: string[];
} | null;
export interface RoletypesGettableResourcesDTO {
/**
* @type object
* @nullable true
*/
relations: RoletypesGettableResourcesDTORelations;
/**
* @type array
* @nullable true
*/
resources: AuthtypesResourceDTO[] | null;
}
export interface RoletypesPatchableObjectsDTO {
/**
* @type array
* @nullable true
*/
additions: AuthtypesObjectDTO[] | null;
/**
* @type array
* @nullable true
*/
deletions: AuthtypesObjectDTO[] | null;
}
export interface RoletypesPatchableRoleDTO {
/**
* @type string
*/
description: string;
}
export interface RoletypesPostableRoleDTO {
/**
* @type string
*/
description?: string;
/**
* @type string
*/
name: string;
}
export interface RoletypesRoleDTO {
/**
* @type string
@@ -2052,7 +1956,7 @@ export interface RoletypesRoleDTO {
/**
* @type string
*/
description: string;
description?: string;
/**
* @type string
*/
@@ -2060,15 +1964,15 @@ export interface RoletypesRoleDTO {
/**
* @type string
*/
name: string;
name?: string;
/**
* @type string
*/
orgId: string;
orgId?: string;
/**
* @type string
*/
type: string;
type?: string;
/**
* @type string
* @format date-time
@@ -2595,17 +2499,6 @@ export interface ZeustypesPostableProfileDTO {
where_did_you_discover_signoz: string;
}
export type AuthzCheck200 = {
/**
* @type array
*/
data?: AuthtypesGettableTransactionDTO[];
/**
* @type string
*/
status?: string;
};
export type ChangePasswordPathParameters = {
id: string;
};
@@ -3009,33 +2902,6 @@ export type GetRole200 = {
export type PatchRolePathParameters = {
id: string;
};
export type GetObjectsPathParameters = {
id: string;
relation: string;
};
export type GetObjects200 = {
/**
* @type array
*/
data?: AuthtypesObjectDTO[];
/**
* @type string
*/
status?: string;
};
export type PatchObjectsPathParameters = {
id: string;
relation: string;
};
export type GetResources200 = {
data?: RoletypesGettableResourcesDTO;
/**
* @type string
*/
status?: string;
};
export type ListUsers200 = {
/**
* @type array

View File

@@ -27,7 +27,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -103,10 +103,9 @@ function K8sClustersList({
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
if (groupBy) {
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
if (parsed) {
return parsed;
}
const decoded = decodeURIComponent(groupBy);
const parsed = JSON.parse(decoded);
return parsed as IBuilderQuery['groupBy'];
}
return [];
});

View File

@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -105,10 +105,9 @@ function K8sDaemonSetsList({
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
if (groupBy) {
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
if (parsed) {
return parsed;
}
const decoded = decodeURIComponent(groupBy);
const parsed = JSON.parse(decoded);
return parsed as IBuilderQuery['groupBy'];
}
return [];
});

View File

@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -106,10 +106,9 @@ function K8sDeploymentsList({
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
if (groupBy) {
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
if (parsed) {
return parsed;
}
const decoded = decodeURIComponent(groupBy);
const parsed = JSON.parse(decoded);
return parsed as IBuilderQuery['groupBy'];
}
return [];
});

View File

@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -101,10 +101,9 @@ function K8sJobsList({
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
if (groupBy) {
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
if (parsed) {
return parsed;
}
const decoded = decodeURIComponent(groupBy);
const parsed = JSON.parse(decoded);
return parsed as IBuilderQuery['groupBy'];
}
return [];
});

View File

@@ -10,7 +10,6 @@ import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteRe
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
import { safeParseJSON } from './commonUtils';
import { INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory } from './constants';
import K8sFiltersSidePanel from './K8sFiltersSidePanel/K8sFiltersSidePanel';
import { IEntityColumn } from './utils';
@@ -59,10 +58,9 @@ function K8sHeader({
const urlFilters = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.FILTERS);
let { filters } = currentQuery.builder.queryData[0];
if (urlFilters) {
const parsed = safeParseJSON<IBuilderQuery['filters']>(urlFilters);
if (parsed) {
filters = parsed;
}
const decoded = decodeURIComponent(urlFilters);
const parsed = JSON.parse(decoded);
filters = parsed;
}
return {
...currentQuery,

View File

@@ -27,7 +27,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -104,10 +104,9 @@ function K8sNamespacesList({
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
if (groupBy) {
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
if (parsed) {
return parsed;
}
const decoded = decodeURIComponent(groupBy);
const parsed = JSON.parse(decoded);
return parsed as IBuilderQuery['groupBy'];
}
return [];
});

View File

@@ -27,7 +27,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -99,10 +99,9 @@ function K8sNodesList({
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
if (groupBy) {
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
if (parsed) {
return parsed;
}
const decoded = decodeURIComponent(groupBy);
const parsed = JSON.parse(decoded);
return parsed as IBuilderQuery['groupBy'];
}
return [];
});

View File

@@ -29,7 +29,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -92,10 +92,9 @@ function K8sPodsList({
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
if (groupBy) {
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
if (parsed) {
return parsed;
}
const decoded = decodeURIComponent(groupBy);
const parsed = JSON.parse(decoded);
return parsed as IBuilderQuery['groupBy'];
}
return [];
});

View File

@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -105,10 +105,9 @@ function K8sStatefulSetsList({
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
if (groupBy) {
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
if (parsed) {
return parsed;
}
const decoded = decodeURIComponent(groupBy);
const parsed = JSON.parse(decoded);
return parsed as IBuilderQuery['groupBy'];
}
return [];
});

View File

@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
import { getOrderByFromParams } from '../commonUtils';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -105,10 +105,9 @@ function K8sVolumesList({
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
if (groupBy) {
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
if (parsed) {
return parsed;
}
const decoded = decodeURIComponent(groupBy);
const parsed = JSON.parse(decoded);
return parsed as IBuilderQuery['groupBy'];
}
return [];
});

View File

@@ -5,7 +5,6 @@
/* eslint-disable prefer-destructuring */
import { useMemo } from 'react';
import * as Sentry from '@sentry/react';
import { Color } from '@signozhq/design-tokens';
import { Table, Tooltip, Typography } from 'antd';
import { Progress } from 'antd/lib';
@@ -261,27 +260,6 @@ export const filterDuplicateFilters = (
return uniqueFilters;
};
export const safeParseJSON = <T,>(value: string): T | null => {
if (!value) {
return null;
}
try {
// Attempting to parse potentially untrusted user input from the URL.
// If the user pastes a corrupted link or modifies the URL manually (e.g., ?filters=invalidJSON),
// JSON.parse() will throw a SyntaxError. Without this try/catch block, that unhandled
// exception would bubble up during the React component render cycle and crash.
return JSON.parse(value) as T;
} catch (e) {
// By catching the SyntaxError, we gracefully degrade the user experience.
// Instead of crashing, the app ignores the malformed URL parameter and cleanly
// falls back to the default state (e.g., no filters applied).
console.error('Error parsing JSON from URL parameter:', e);
// TODO: Should we capture this error in Sentry?
return null;
}
};
export const getOrderByFromParams = (
searchParams: URLSearchParams,
returnNullAsDefault = false,
@@ -293,12 +271,9 @@ export const getOrderByFromParams = (
INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY,
);
if (orderByFromParams) {
const parsed = safeParseJSON<{ columnName: string; order: 'asc' | 'desc' }>(
orderByFromParams,
);
if (parsed) {
return parsed;
}
const decoded = decodeURIComponent(orderByFromParams);
const parsed = JSON.parse(decoded);
return parsed as { columnName: string; order: 'asc' | 'desc' };
}
if (returnNullAsDefault) {
return null;
@@ -312,7 +287,13 @@ export const getFiltersFromParams = (
): IBuilderQuery['filters'] | null => {
const filtersFromParams = searchParams.get(queryKey);
if (filtersFromParams) {
return safeParseJSON<IBuilderQuery['filters']>(filtersFromParams);
try {
const decoded = decodeURIComponent(filtersFromParams);
const parsed = JSON.parse(decoded);
return parsed as IBuilderQuery['filters'];
} catch (error) {
return null;
}
}
return null;
};

View File

@@ -1,30 +0,0 @@
package signozapiserver
import (
"net/http"
"github.com/SigNoz/signoz/pkg/http/handler"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/gorilla/mux"
)
func (provider *provider) addAuthzRoutes(router *mux.Router) error {
if err := router.Handle("/api/v1/authz/check", handler.New(provider.authzHandler.Check, handler.OpenAPIDef{
ID: "AuthzCheck",
Tags: []string{"authz"},
Summary: "Check permissions",
Description: "Checks if the authenticated user has permissions for given transactions",
Request: make([]*authtypes.Transaction, 0),
RequestContentType: "",
Response: make([]*authtypes.GettableTransaction, 0),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{},
Deprecated: false,
SecuritySchemes: nil,
})).Methods(http.MethodPost).GetError(); err != nil {
return err
}
return nil
}

View File

@@ -207,10 +207,6 @@ func (provider *provider) AddToRouter(router *mux.Router) error {
return err
}
if err := provider.addAuthzRoutes(router); err != nil {
return err
}
if err := provider.addFieldsRoutes(router); err != nil {
return err
}

View File

@@ -5,7 +5,6 @@ import (
"github.com/SigNoz/signoz/pkg/http/handler"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/types/roletypes"
"github.com/gorilla/mux"
)
@@ -16,12 +15,12 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
Tags: []string{"role"},
Summary: "Create role",
Description: "This endpoint creates a role",
Request: new(roletypes.PostableRole),
Request: nil,
RequestContentType: "",
Response: new(types.Identifiable),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusCreated,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
ErrorStatusCodes: []int{},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
})).Methods(http.MethodPost).GetError(); err != nil {
@@ -45,23 +44,6 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
return err
}
if err := router.Handle("/api/v1/roles/resources", handler.New(provider.authZ.AdminAccess(provider.authzHandler.GetResources), handler.OpenAPIDef{
ID: "GetResources",
Tags: []string{"role"},
Summary: "Get resources",
Description: "Gets all the available resources for role assignment",
Request: nil,
RequestContentType: "",
Response: new(roletypes.GettableResources),
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.authzHandler.Get), handler.OpenAPIDef{
ID: "GetRole",
Tags: []string{"role"},
@@ -79,51 +61,17 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
return err
}
if err := router.Handle("/api/v1/roles/{id}/relation/{relation}/objects", handler.New(provider.authZ.AdminAccess(provider.authzHandler.GetObjects), handler.OpenAPIDef{
ID: "GetObjects",
Tags: []string{"role"},
Summary: "Get objects for a role by relation",
Description: "Gets all objects connected to the specified role via a given relation type",
Request: nil,
RequestContentType: "",
Response: make([]*authtypes.Object, 0),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
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.authzHandler.Patch), handler.OpenAPIDef{
ID: "PatchRole",
Tags: []string{"role"},
Summary: "Patch role",
Description: "This endpoint patches a role",
Request: new(roletypes.PatchableRole),
Request: nil,
RequestContentType: "",
Response: nil,
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusNoContent,
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
})).Methods(http.MethodPatch).GetError(); err != nil {
return err
}
if err := router.Handle("/api/v1/roles/{id}/relation/{relation}/objects", handler.New(provider.authZ.AdminAccess(provider.authzHandler.PatchObjects), handler.OpenAPIDef{
ID: "PatchObjects",
Tags: []string{"role"},
Summary: "Patch objects for a role by relation",
Description: "Patches the objects connected to the specified role via a given relation type",
Request: new(roletypes.PatchableObjects),
RequestContentType: "",
Response: nil,
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusNoContent,
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusBadRequest, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
ErrorStatusCodes: []int{},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
})).Methods(http.MethodPatch).GetError(); err != nil {
@@ -140,7 +88,7 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
Response: nil,
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusNoContent,
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
ErrorStatusCodes: []int{},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
})).Methods(http.MethodDelete).GetError(); err != nil {

View File

@@ -14,14 +14,17 @@ import (
type AuthZ interface {
factory.Service
// Check returns error when the upstream authorization server is unavailable or the subject (s) doesn't have relation (r) on object (o).
Check(context.Context, *openfgav1.TupleKey) error
// CheckWithTupleCreation takes upon the responsibility for generating the tuples alongside everything Check does.
CheckWithTupleCreation(context.Context, authtypes.Claims, valuer.UUID, authtypes.Relation, authtypes.Typeable, []authtypes.Selector, []authtypes.Selector) error
// CheckWithTupleCreationWithoutClaims checks permissions for anonymous users.
CheckWithTupleCreationWithoutClaims(context.Context, valuer.UUID, authtypes.Relation, authtypes.Typeable, []authtypes.Selector, []authtypes.Selector) error
// BatchCheck accepts a map of ID → tuple and returns a map of ID → authorization result.
BatchCheck(context.Context, map[string]*openfgav1.TupleKey) (map[string]*authtypes.TupleKeyAuthorization, 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).
BatchCheck(context.Context, []*openfgav1.TupleKey) error
// Write accepts the insertion tuples and the deletion tuples.
Write(context.Context, []*openfgav1.TupleKey, []*openfgav1.TupleKey) error
@@ -99,7 +102,5 @@ type Handler interface {
PatchObjects(http.ResponseWriter, *http.Request)
Check(http.ResponseWriter, *http.Request)
Delete(http.ResponseWriter, *http.Request)
}

View File

@@ -26,7 +26,7 @@ func (store *store) Create(ctx context.Context, role *roletypes.StorableRole) er
Model(role).
Exec(ctx)
if err != nil {
return store.sqlstore.WrapAlreadyExistsErrf(err, errors.CodeAlreadyExists, "role with name: %s already exists", role.Name)
return store.sqlstore.WrapAlreadyExistsErrf(err, errors.CodeAlreadyExists, "role with id: %s already exists", role.ID)
}
return nil

View File

@@ -48,7 +48,11 @@ func (provider *provider) Stop(ctx context.Context) error {
return provider.server.Stop(ctx)
}
func (provider *provider) BatchCheck(ctx context.Context, tupleReq map[string]*openfgav1.TupleKey) (map[string]*authtypes.TupleKeyAuthorization, error) {
func (provider *provider) Check(ctx context.Context, tupleReq *openfgav1.TupleKey) error {
return provider.server.Check(ctx, tupleReq)
}
func (provider *provider) BatchCheck(ctx context.Context, tupleReq []*openfgav1.TupleKey) error {
return provider.server.BatchCheck(ctx, tupleReq)
}
@@ -177,6 +181,10 @@ func (provider *provider) CreateManagedRoles(ctx context.Context, _ valuer.UUID,
return nil
}
func (provider *provider) SetManagedRoleTransactions(context.Context, valuer.UUID) error {
return nil
}
func (provider *provider) CreateManagedUserRoleTransactions(ctx context.Context, orgID valuer.UUID, userID valuer.UUID) error {
return provider.Grant(ctx, orgID, roletypes.SigNozAdminRoleName, authtypes.MustNewSubject(authtypes.TypeableUser, userID.String(), orgID, nil))
}

View File

@@ -90,18 +90,42 @@ func (server *Server) Stop(ctx context.Context) error {
return nil
}
func (server *Server) BatchCheck(ctx context.Context, tupleReq map[string]*openfgav1.TupleKey) (map[string]*authtypes.TupleKeyAuthorization, error) {
func (server *Server) Check(ctx context.Context, tupleReq *openfgav1.TupleKey) error {
storeID, modelID := server.getStoreIDandModelID()
batchCheckItems := make([]*openfgav1.BatchCheckItem, 0, len(tupleReq))
for id, tuple := range tupleReq {
checkResponse, err := server.openfgaServer.Check(
ctx,
&openfgav1.CheckRequest{
StoreId: storeID,
AuthorizationModelId: modelID,
TupleKey: &openfgav1.CheckRequestTupleKey{
User: tupleReq.User,
Relation: tupleReq.Relation,
Object: tupleReq.Object,
},
})
if err != nil {
return errors.Newf(errors.TypeInternal, authtypes.ErrCodeAuthZUnavailable, "authorization server is unavailable").WithAdditional(err.Error())
}
if !checkResponse.Allowed {
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "subject %s cannot %s object %s", tupleReq.User, tupleReq.Relation, tupleReq.Object)
}
return nil
}
func (server *Server) BatchCheck(ctx context.Context, tupleReq []*openfgav1.TupleKey) error {
storeID, modelID := server.getStoreIDandModelID()
batchCheckItems := make([]*openfgav1.BatchCheckItem, 0)
for idx, tuple := range tupleReq {
batchCheckItems = append(batchCheckItems, &openfgav1.BatchCheckItem{
TupleKey: &openfgav1.CheckRequestTupleKey{
User: tuple.User,
Relation: tuple.Relation,
Object: tuple.Object,
},
// Use transaction ID as correlation ID for deterministic mapping
CorrelationId: id,
// the batch check response is map[string] keyed by correlationID.
CorrelationId: strconv.Itoa(idx),
})
}
@@ -113,18 +137,17 @@ func (server *Server) BatchCheck(ctx context.Context, tupleReq map[string]*openf
Checks: batchCheckItems,
})
if err != nil {
return nil, errors.Newf(errors.TypeInternal, authtypes.ErrCodeAuthZUnavailable, "authorization server is unavailable").WithAdditional(err.Error())
return errors.Newf(errors.TypeInternal, authtypes.ErrCodeAuthZUnavailable, "authorization server is unavailable").WithAdditional(err.Error())
}
response := make(map[string]*authtypes.TupleKeyAuthorization, len(tupleReq))
for id, tuple := range tupleReq {
response[id] = &authtypes.TupleKeyAuthorization{
Tuple: tuple,
Authorized: checkResponse.Result[id].GetAllowed(),
for _, checkResponse := range checkResponse.Result {
if checkResponse.GetAllowed() {
return nil
}
}
return response, nil
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "subjects are not authorized for requested access")
}
func (server *Server) CheckWithTupleCreation(ctx context.Context, claims authtypes.Claims, orgID valuer.UUID, _ authtypes.Relation, _ authtypes.Typeable, _ []authtypes.Selector, roleSelectors []authtypes.Selector) error {
@@ -133,29 +156,17 @@ func (server *Server) CheckWithTupleCreation(ctx context.Context, claims authtyp
return err
}
tupleSlice, err := authtypes.TypeableRole.Tuples(subject, authtypes.RelationAssignee, roleSelectors, orgID)
tuples, err := authtypes.TypeableRole.Tuples(subject, authtypes.RelationAssignee, roleSelectors, orgID)
if err != nil {
return err
}
// Convert slice to map with generated IDs for internal use
tuples := make(map[string]*openfgav1.TupleKey, len(tupleSlice))
for idx, tuple := range tupleSlice {
tuples[strconv.Itoa(idx)] = tuple
}
response, err := server.BatchCheck(ctx, tuples)
err = server.BatchCheck(ctx, tuples)
if err != nil {
return err
}
for _, resp := range response {
if resp.Authorized {
return nil
}
}
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "subjects are not authorized for requested access")
return nil
}
func (server *Server) CheckWithTupleCreationWithoutClaims(ctx context.Context, orgID valuer.UUID, _ authtypes.Relation, _ authtypes.Typeable, _ []authtypes.Selector, roleSelectors []authtypes.Selector) error {
@@ -164,29 +175,17 @@ func (server *Server) CheckWithTupleCreationWithoutClaims(ctx context.Context, o
return err
}
tupleSlice, err := authtypes.TypeableRole.Tuples(subject, authtypes.RelationAssignee, roleSelectors, orgID)
tuples, err := authtypes.TypeableRole.Tuples(subject, authtypes.RelationAssignee, roleSelectors, orgID)
if err != nil {
return err
}
// Convert slice to map with generated IDs for internal use
tuples := make(map[string]*openfgav1.TupleKey, len(tupleSlice))
for idx, tuple := range tupleSlice {
tuples[strconv.Itoa(idx)] = tuple
}
response, err := server.BatchCheck(ctx, tuples)
err = server.BatchCheck(ctx, tuples)
if err != nil {
return err
}
for _, resp := range response {
if resp.Authorized {
return nil
}
}
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "subjects are not authorized for requested access")
return nil
}
func (server *Server) Write(ctx context.Context, additions []*openfgav1.TupleKey, deletions []*openfgav1.TupleKey) error {

View File

@@ -7,7 +7,6 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/http/binding"
"github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/types/roletypes"
"github.com/SigNoz/signoz/pkg/valuer"
@@ -36,14 +35,13 @@ func (handler *handler) Create(rw http.ResponseWriter, r *http.Request) {
return
}
role := roletypes.NewRole(req.Name, req.Description, roletypes.RoleTypeCustom, valuer.MustNewUUID(claims.OrgID))
err = handler.authz.Create(ctx, valuer.MustNewUUID(claims.OrgID), role)
err = handler.authz.Create(ctx, valuer.MustNewUUID(claims.OrgID), roletypes.NewRole(req.Name, req.Description, roletypes.RoleTypeCustom, valuer.MustNewUUID(claims.OrgID)))
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusCreated, types.Identifiable{ID: role.ID})
render.Success(rw, http.StatusCreated, nil)
}
func (handler *handler) Get(rw http.ResponseWriter, r *http.Request) {
@@ -114,9 +112,17 @@ func (handler *handler) GetObjects(rw http.ResponseWriter, r *http.Request) {
}
func (handler *handler) GetResources(rw http.ResponseWriter, r *http.Request) {
resources := handler.authz.GetResources(r.Context())
ctx := r.Context()
resources := handler.authz.GetResources(ctx)
render.Success(rw, http.StatusOK, roletypes.NewGettableResources(resources))
var resourceRelations = struct {
Resources []*authtypes.Resource `json:"resources"`
Relations map[authtypes.Type][]authtypes.Relation `json:"relations"`
}{
Resources: resources,
Relations: authtypes.TypeableRelations,
}
render.Success(rw, http.StatusOK, resourceRelations)
}
func (handler *handler) List(rw http.ResponseWriter, r *http.Request) {
@@ -221,7 +227,7 @@ func (handler *handler) PatchObjects(rw http.ResponseWriter, r *http.Request) {
return
}
render.Success(rw, http.StatusNoContent, nil)
render.Success(rw, http.StatusAccepted, nil)
}
func (handler *handler) Delete(rw http.ResponseWriter, r *http.Request) {
@@ -246,39 +252,3 @@ func (handler *handler) Delete(rw http.ResponseWriter, r *http.Request) {
render.Success(rw, http.StatusNoContent, nil)
}
func (handler *handler) Check(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
transactions := make([]*authtypes.Transaction, 0)
if err := binding.JSON.BindBody(r.Body, &transactions); err != nil {
render.Error(rw, err)
return
}
orgID := valuer.MustNewUUID(claims.OrgID)
subject, err := authtypes.NewSubject(authtypes.TypeableUser, claims.UserID, orgID, nil)
if err != nil {
render.Error(rw, err)
return
}
tuples, err := authtypes.NewTuplesFromTransactions(transactions, subject, orgID)
if err != nil {
render.Error(rw, err)
return
}
results, err := handler.authz.BatchCheck(ctx, tuples)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, authtypes.NewGettableTransaction(transactions, results))
}

View File

@@ -3,8 +3,9 @@ package flagger
import "github.com/SigNoz/signoz/pkg/types/featuretypes"
var (
FeatureUseSpanMetrics = featuretypes.MustNewName("use_span_metrics")
FeatureKafkaSpanEval = featuretypes.MustNewName("kafka_span_eval")
FeatureUseSpanMetrics = featuretypes.MustNewName("use_span_metrics")
FeatureKafkaSpanEval = featuretypes.MustNewName("kafka_span_eval")
FeatureListUsersIncludeRoot = featuretypes.MustNewName("list_users_include_root")
)
func MustNewRegistry() featuretypes.Registry {
@@ -25,6 +26,14 @@ func MustNewRegistry() featuretypes.Registry {
DefaultVariant: featuretypes.MustNewName("disabled"),
Variants: featuretypes.NewBooleanVariants(),
},
&featuretypes.Feature{
Name: FeatureListUsersIncludeRoot,
Kind: featuretypes.KindBoolean,
Stage: featuretypes.StageStable,
Description: "Controls whether root admin users are shown in the list users API",
DefaultVariant: featuretypes.MustNewName("enabled"),
Variants: featuretypes.NewBooleanVariants(),
},
)
if err != nil {
panic(err)

View File

@@ -8,22 +8,25 @@ import (
"time"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/flagger"
"github.com/SigNoz/signoz/pkg/http/binding"
"github.com/SigNoz/signoz/pkg/http/render"
root "github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/types/featuretypes"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/gorilla/mux"
)
type handler struct {
module root.Module
getter root.Getter
module root.Module
getter root.Getter
flagger flagger.Flagger
}
func NewHandler(module root.Module, getter root.Getter) root.Handler {
return &handler{module: module, getter: getter}
func NewHandler(module root.Module, getter root.Getter, flagger flagger.Flagger) root.Handler {
return &handler{module: module, getter: getter, flagger: flagger}
}
func (h *handler) AcceptInvite(w http.ResponseWriter, r *http.Request) {
@@ -211,12 +214,29 @@ func (h *handler) ListUsers(w http.ResponseWriter, r *http.Request) {
return
}
users, err := h.getter.ListByOrgID(ctx, valuer.MustNewUUID(claims.OrgID))
orgId := valuer.MustNewUUID(claims.OrgID)
users, err := h.getter.ListByOrgID(ctx, orgId)
if err != nil {
render.Error(w, err)
return
}
// filter root users if feature flag `list_users_include_root` is disabled
evalCtx := featuretypes.NewFlaggerEvaluationContext(orgId)
listUsersIncludeRoot := h.flagger.BooleanOrEmpty(r.Context(), flagger.FeatureListUsersIncludeRoot, evalCtx)
if !listUsersIncludeRoot {
filteredUsers := users[:0]
for _, u := range users {
if !u.IsRoot {
filteredUsers = append(filteredUsers, u)
}
}
render.Success(w, http.StatusOK, filteredUsers)
return
}
render.Success(w, http.StatusOK, users)
}

View File

@@ -11,6 +11,8 @@ import (
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/emailing/emailingtest"
"github.com/SigNoz/signoz/pkg/factory/factorytest"
"github.com/SigNoz/signoz/pkg/flagger"
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
"github.com/SigNoz/signoz/pkg/querier"
@@ -41,7 +43,13 @@ func TestNewHandlers(t *testing.T) {
queryParser := queryparser.New(providerSettings)
require.NoError(t, err)
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)
flagger, err := flagger.New(context.Background(), instrumentationtest.New().ToProviderSettings(), flagger.Config{}, flagger.MustNewRegistry())
if err != nil {
t.Fatalf("failed to create flagger: %v", err)
}
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, nil, nil, nil, nil, nil, nil, nil, queryParser, Config{}, dashboardModule, flagger)
querierHandler := querier.NewHandler(providerSettings, nil, nil)
handlers := NewHandlers(modules, providerSettings, nil, querierHandler, nil, nil, nil, nil, nil, nil, nil)

View File

@@ -8,6 +8,7 @@ import (
"github.com/SigNoz/signoz/pkg/cache"
"github.com/SigNoz/signoz/pkg/emailing"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/flagger"
"github.com/SigNoz/signoz/pkg/modules/apdex"
"github.com/SigNoz/signoz/pkg/modules/apdex/implapdex"
"github.com/SigNoz/signoz/pkg/modules/authdomain"
@@ -66,6 +67,7 @@ type Modules struct {
SpanPercentile spanpercentile.Module
MetricsExplorer metricsexplorer.Module
Promote promote.Module
Flagger flagger.Flagger
}
func NewModules(
@@ -85,6 +87,7 @@ func NewModules(
queryParser queryparser.QueryParser,
config Config,
dashboard dashboard.Module,
flagger flagger.Flagger,
) Modules {
quickfilter := implquickfilter.NewModule(implquickfilter.NewStore(sqlstore))
orgSetter := implorganization.NewSetter(implorganization.NewStore(sqlstore), alertmanager, quickfilter)
@@ -110,5 +113,6 @@ func NewModules(
Services: implservices.NewModule(querier, telemetryStore),
MetricsExplorer: implmetricsexplorer.NewModule(telemetryStore, telemetryMetadataStore, cache, ruleStore, dashboard, providerSettings, config.MetricsExplorer),
Promote: implpromote.NewModule(telemetryMetadataStore, telemetryStore),
Flagger: flagger,
}
}

View File

@@ -11,6 +11,8 @@ import (
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/emailing/emailingtest"
"github.com/SigNoz/signoz/pkg/factory/factorytest"
"github.com/SigNoz/signoz/pkg/flagger"
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
"github.com/SigNoz/signoz/pkg/modules/organization/implorganization"
"github.com/SigNoz/signoz/pkg/queryparser"
@@ -40,7 +42,13 @@ func TestNewModules(t *testing.T) {
queryParser := queryparser.New(providerSettings)
require.NoError(t, err)
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)
flagger, err := flagger.New(context.Background(), instrumentationtest.New().ToProviderSettings(), flagger.Config{}, flagger.MustNewRegistry())
if err != nil {
t.Fatalf("failed to create flagger: %v", err)
}
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, nil, nil, nil, nil, nil, nil, nil, queryParser, Config{}, dashboardModule, flagger)
reflectVal := reflect.ValueOf(modules)
for i := 0; i < reflectVal.NumField(); i++ {

View File

@@ -239,7 +239,7 @@ func NewAPIServerProviderFactories(orgGetter organization.Getter, authz authz.Au
orgGetter,
authz,
implorganization.NewHandler(modules.OrgGetter, modules.OrgSetter),
impluser.NewHandler(modules.User, modules.UserGetter),
impluser.NewHandler(modules.User, modules.UserGetter, modules.Flagger),
implsession.NewHandler(modules.Session),
implauthdomain.NewHandler(modules.AuthDomain),
implpreference.NewHandler(modules.Preference),

View File

@@ -388,7 +388,7 @@ func New(
}
// Initialize all modules
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, analytics, querier, telemetrystore, telemetryMetadataStore, authNs, authz, cache, queryParser, config, dashboard)
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, analytics, querier, telemetrystore, telemetryMetadataStore, authNs, authz, cache, queryParser, config, dashboard, flagger)
userService := impluser.NewService(providerSettings, impluser.NewStore(sqlstore, providerSettings), modules.User, orgGetter, authz, config.User.Root)

View File

@@ -15,14 +15,15 @@ var (
RelationUpdate = Relation{valuer.NewString("update")}
RelationDelete = Relation{valuer.NewString("delete")}
RelationList = Relation{valuer.NewString("list")}
RelationBlock = Relation{valuer.NewString("block")}
RelationAssignee = Relation{valuer.NewString("assignee")}
)
var TypeableRelations = map[Type][]Relation{
TypeUser: {RelationRead, RelationUpdate, RelationDelete},
TypeRole: {RelationAssignee, RelationRead, RelationUpdate, RelationDelete},
TypeOrganization: {RelationRead, RelationUpdate, RelationDelete},
TypeMetaResource: {RelationRead, RelationUpdate, RelationDelete},
TypeOrganization: {RelationCreate, RelationRead, RelationUpdate, RelationDelete, RelationList},
TypeMetaResource: {RelationRead, RelationUpdate, RelationDelete, RelationBlock},
TypeMetaResources: {RelationCreate, RelationList},
}
@@ -40,6 +41,8 @@ func NewRelation(relation string) (Relation, error) {
return RelationDelete, nil
case "list":
return RelationList, nil
case "block":
return RelationBlock, nil
case "assignee":
return RelationAssignee, nil
default:

View File

@@ -6,29 +6,21 @@ import (
"strings"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/valuer"
)
type Resource struct {
Name Name `json:"name" required:"true"`
Type Type `json:"type" required:"true"`
Name Name `json:"name"`
Type Type `json:"type"`
}
type Object struct {
Resource Resource `json:"resource" required:"true"`
Selector Selector `json:"selector" required:"true"`
Resource Resource `json:"resource"`
Selector Selector `json:"selector"`
}
type Transaction struct {
ID valuer.UUID `json:"id"`
Relation Relation `json:"relation" required:"true"`
Object Object `json:"object" required:"true"`
}
type GettableTransaction struct {
Relation Relation `json:"relation" required:"true"`
Object Object `json:"object" required:"true"`
Authorized bool `json:"authorized" required:"true"`
Relation Relation `json:"relation"`
Object Object `json:"object"`
}
func NewObject(resource Resource, selector Selector) (*Object, error) {
@@ -83,21 +75,7 @@ func NewTransaction(relation Relation, object Object) (*Transaction, error) {
return nil, errors.Newf(errors.TypeInvalidInput, ErrCodeAuthZInvalidRelation, "invalid relation %s for type %s", relation.StringValue(), object.Resource.Type.StringValue())
}
return &Transaction{ID: valuer.GenerateUUID(), Relation: relation, Object: object}, nil
}
func NewGettableTransaction(transactions []*Transaction, results map[string]*TupleKeyAuthorization) []*GettableTransaction {
gettableTransactions := make([]*GettableTransaction, len(transactions))
for i, txn := range transactions {
result := results[txn.ID.StringValue()]
gettableTransactions[i] = &GettableTransaction{
Relation: txn.Relation,
Object: txn.Object,
Authorized: result.Authorized,
}
}
return gettableTransactions
return &Transaction{Relation: relation, Object: object}, nil
}
func (object *Object) UnmarshalJSON(data []byte) error {

View File

@@ -1,31 +0,0 @@
package authtypes
import (
"github.com/SigNoz/signoz/pkg/valuer"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
)
type TupleKeyAuthorization struct {
Tuple *openfgav1.TupleKey
Authorized bool
}
func NewTuplesFromTransactions(transactions []*Transaction, subject string, orgID valuer.UUID) (map[string]*openfgav1.TupleKey, error) {
tuples := make(map[string]*openfgav1.TupleKey, len(transactions))
for _, txn := range transactions {
typeable, err := NewTypeableFromType(txn.Object.Resource.Type, txn.Object.Resource.Name)
if err != nil {
return nil, err
}
txnTuples, err := typeable.Tuples(subject, txn.Relation, []Selector{txn.Object.Selector}, orgID)
if err != nil {
return nil, err
}
// Each transaction produces one tuple, keyed by transaction ID
tuples[txn.ID.StringValue()] = txnTuples[0]
}
return tuples, nil
}

View File

@@ -69,29 +69,24 @@ type StorableRole struct {
type Role struct {
types.Identifiable
types.TimeAuditable
Name string `json:"name" required:"true"`
Description string `json:"description" required:"true"`
Type valuer.String `json:"type" required:"true"`
OrgID valuer.UUID `json:"orgId" required:"true"`
Name string `json:"name"`
Description string `json:"description"`
Type valuer.String `json:"type"`
OrgID valuer.UUID `json:"orgId"`
}
type PostableRole struct {
Name string `json:"name" required:"true"`
Name string `json:"name"`
Description string `json:"description"`
}
type PatchableRole struct {
Description string `json:"description" required:"true"`
Description *string `json:"description"`
}
type PatchableObjects struct {
Additions []*authtypes.Object `json:"additions" required:"true"`
Deletions []*authtypes.Object `json:"deletions" required:"true"`
}
type GettableResources struct {
Resources []*authtypes.Resource `json:"resources" required:"true"`
Relations map[authtypes.Type][]authtypes.Relation `json:"relations" required:"true"`
Additions []*authtypes.Object `json:"additions"`
Deletions []*authtypes.Object `json:"deletions"`
}
func NewStorableRoleFromRole(role *Role) *StorableRole {
@@ -142,20 +137,15 @@ func NewManagedRoles(orgID valuer.UUID) []*Role {
}
func NewGettableResources(resources []*authtypes.Resource) *GettableResources {
return &GettableResources{
Resources: resources,
Relations: authtypes.TypeableRelations,
}
}
func (role *Role) PatchMetadata(description string) error {
func (role *Role) PatchMetadata(description *string) error {
err := role.CanEditDelete()
if err != nil {
return err
}
role.Description = description
if description != nil {
role.Description = *description
}
role.UpdatedAt = time.Now()
return nil
}
@@ -220,7 +210,7 @@ func (role *PostableRole) UnmarshalJSON(data []byte) error {
func (role *PatchableRole) UnmarshalJSON(data []byte) error {
type shadowPatchableRole struct {
Description string `json:"description"`
Description *string `json:"description"`
}
var shadowRole shadowPatchableRole
@@ -228,7 +218,7 @@ func (role *PatchableRole) UnmarshalJSON(data []byte) error {
return err
}
if shadowRole.Description == "" {
if shadowRole.Description == nil {
return errors.New(errors.TypeInvalidInput, ErrCodeRoleEmptyPatch, "empty role patch request received, description must be present")
}