mirror of
https://github.com/SigNoz/signoz.git
synced 2026-02-21 16:13:37 +00:00
Compare commits
6 Commits
fix/remove
...
invite-val
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39de574ef9 | ||
|
|
76f89386a0 | ||
|
|
ab80257235 | ||
|
|
3f66a6fc14 | ||
|
|
723eaa2f8e | ||
|
|
0f9427765b |
@@ -92,19 +92,6 @@ components:
|
|||||||
tokenType:
|
tokenType:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
AuthtypesGettableTransaction:
|
|
||||||
properties:
|
|
||||||
authorized:
|
|
||||||
type: boolean
|
|
||||||
object:
|
|
||||||
$ref: '#/components/schemas/AuthtypesObject'
|
|
||||||
relation:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- relation
|
|
||||||
- object
|
|
||||||
- authorized
|
|
||||||
type: object
|
|
||||||
AuthtypesGoogleConfig:
|
AuthtypesGoogleConfig:
|
||||||
properties:
|
properties:
|
||||||
allowedGroups:
|
allowedGroups:
|
||||||
@@ -130,8 +117,6 @@ components:
|
|||||||
serviceAccountJson:
|
serviceAccountJson:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
AuthtypesName:
|
|
||||||
type: object
|
|
||||||
AuthtypesOIDCConfig:
|
AuthtypesOIDCConfig:
|
||||||
properties:
|
properties:
|
||||||
claimMapping:
|
claimMapping:
|
||||||
@@ -149,16 +134,6 @@ components:
|
|||||||
issuerAlias:
|
issuerAlias:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
AuthtypesObject:
|
|
||||||
properties:
|
|
||||||
resource:
|
|
||||||
$ref: '#/components/schemas/AuthtypesResource'
|
|
||||||
selector:
|
|
||||||
$ref: '#/components/schemas/AuthtypesSelector'
|
|
||||||
required:
|
|
||||||
- resource
|
|
||||||
- selector
|
|
||||||
type: object
|
|
||||||
AuthtypesOrgSessionContext:
|
AuthtypesOrgSessionContext:
|
||||||
properties:
|
properties:
|
||||||
authNSupport:
|
authNSupport:
|
||||||
@@ -196,16 +171,6 @@ components:
|
|||||||
refreshToken:
|
refreshToken:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
AuthtypesResource:
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
$ref: '#/components/schemas/AuthtypesName'
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- name
|
|
||||||
- type
|
|
||||||
type: object
|
|
||||||
AuthtypesRoleMapping:
|
AuthtypesRoleMapping:
|
||||||
properties:
|
properties:
|
||||||
defaultRole:
|
defaultRole:
|
||||||
@@ -231,8 +196,6 @@ components:
|
|||||||
samlIdp:
|
samlIdp:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
AuthtypesSelector:
|
|
||||||
type: object
|
|
||||||
AuthtypesSessionContext:
|
AuthtypesSessionContext:
|
||||||
properties:
|
properties:
|
||||||
exists:
|
exists:
|
||||||
@@ -243,18 +206,6 @@ components:
|
|||||||
nullable: true
|
nullable: true
|
||||||
type: array
|
type: array
|
||||||
type: object
|
type: object
|
||||||
AuthtypesTransaction:
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: string
|
|
||||||
object:
|
|
||||||
$ref: '#/components/schemas/AuthtypesObject'
|
|
||||||
relation:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- relation
|
|
||||||
- object
|
|
||||||
type: object
|
|
||||||
AuthtypesUpdateableAuthDomain:
|
AuthtypesUpdateableAuthDomain:
|
||||||
properties:
|
properties:
|
||||||
config:
|
config:
|
||||||
@@ -1662,56 +1613,6 @@ components:
|
|||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
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:
|
RoletypesRole:
|
||||||
properties:
|
properties:
|
||||||
createdAt:
|
createdAt:
|
||||||
@@ -1730,11 +1631,6 @@ components:
|
|||||||
updatedAt:
|
updatedAt:
|
||||||
format: date-time
|
format: date-time
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- name
|
|
||||||
- description
|
|
||||||
- type
|
|
||||||
- orgId
|
|
||||||
type: object
|
type: object
|
||||||
TelemetrytypesFieldContext:
|
TelemetrytypesFieldContext:
|
||||||
enum:
|
enum:
|
||||||
@@ -2126,41 +2022,6 @@ info:
|
|||||||
version: ""
|
version: ""
|
||||||
openapi: 3.0.3
|
openapi: 3.0.3
|
||||||
paths:
|
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}:
|
/api/v1/changePassword/{id}:
|
||||||
post:
|
post:
|
||||||
deprecated: false
|
deprecated: false
|
||||||
@@ -3990,11 +3851,6 @@ paths:
|
|||||||
deprecated: false
|
deprecated: false
|
||||||
description: This endpoint creates a role
|
description: This endpoint creates a role
|
||||||
operationId: CreateRole
|
operationId: CreateRole
|
||||||
requestBody:
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/RoletypesPostableRole'
|
|
||||||
responses:
|
responses:
|
||||||
"201":
|
"201":
|
||||||
content:
|
content:
|
||||||
@@ -4007,12 +3863,6 @@ paths:
|
|||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
description: Created
|
description: Created
|
||||||
"400":
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/RenderErrorResponse'
|
|
||||||
description: Bad Request
|
|
||||||
"401":
|
"401":
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
@@ -4025,30 +3875,12 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/RenderErrorResponse'
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
description: Forbidden
|
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":
|
"500":
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/RenderErrorResponse'
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
description: Internal Server Error
|
description: Internal Server Error
|
||||||
"501":
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/RenderErrorResponse'
|
|
||||||
description: Not Implemented
|
|
||||||
security:
|
security:
|
||||||
- api_key:
|
- api_key:
|
||||||
- ADMIN
|
- ADMIN
|
||||||
@@ -4087,30 +3919,12 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/RenderErrorResponse'
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
description: Forbidden
|
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":
|
"500":
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/RenderErrorResponse'
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
description: Internal Server Error
|
description: Internal Server Error
|
||||||
"501":
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/RenderErrorResponse'
|
|
||||||
description: Not Implemented
|
|
||||||
security:
|
security:
|
||||||
- api_key:
|
- api_key:
|
||||||
- ADMIN
|
- ADMIN
|
||||||
@@ -4177,11 +3991,6 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
requestBody:
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/RoletypesPatchableRole'
|
|
||||||
responses:
|
responses:
|
||||||
"204":
|
"204":
|
||||||
content:
|
content:
|
||||||
@@ -4201,30 +4010,12 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/RenderErrorResponse'
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
description: Forbidden
|
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":
|
"500":
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/RenderErrorResponse'
|
$ref: '#/components/schemas/RenderErrorResponse'
|
||||||
description: Internal Server Error
|
description: Internal Server Error
|
||||||
"501":
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/RenderErrorResponse'
|
|
||||||
description: Not Implemented
|
|
||||||
security:
|
security:
|
||||||
- api_key:
|
- api_key:
|
||||||
- ADMIN
|
- ADMIN
|
||||||
@@ -4233,202 +4024,6 @@ paths:
|
|||||||
summary: Patch role
|
summary: Patch role
|
||||||
tags:
|
tags:
|
||||||
- role
|
- 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:
|
/api/v1/user:
|
||||||
get:
|
get:
|
||||||
deprecated: false
|
deprecated: false
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ func (provider *provider) Stop(ctx context.Context) error {
|
|||||||
return provider.openfgaServer.Stop(ctx)
|
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 {
|
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)
|
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)
|
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) {
|
func (provider *provider) BatchCheck(ctx context.Context, tuples []*openfgav1.TupleKey) error {
|
||||||
return provider.openfgaServer.BatchCheck(ctx, tupleReq)
|
return provider.openfgaServer.BatchCheck(ctx, tuples)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *provider) ListObjects(ctx context.Context, subject string, relation authtypes.Relation, typeable authtypes.Typeable) ([]*authtypes.Object, error) {
|
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) {
|
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)
|
storableRole, err := provider.store.Get(ctx, orgID, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -199,7 +198,7 @@ func (provider *provider) GetObjects(ctx context.Context, orgID valuer.UUID, id
|
|||||||
resourceObjects, err := provider.
|
resourceObjects, err := provider.
|
||||||
ListObjects(
|
ListObjects(
|
||||||
ctx,
|
ctx,
|
||||||
authtypes.MustNewSubject(authtypes.TypeableRole, storableRole.Name, orgID, &authtypes.RelationAssignee),
|
authtypes.MustNewSubject(authtypes.TypeableRole, storableRole.ID.String(), orgID, &authtypes.RelationAssignee),
|
||||||
relation,
|
relation,
|
||||||
authtypes.MustNewTypeableFromType(resource.Type, resource.Name),
|
authtypes.MustNewTypeableFromType(resource.Type, resource.Name),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ package openfgaserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/authz"
|
"github.com/SigNoz/signoz/pkg/authz"
|
||||||
"github.com/SigNoz/signoz/pkg/errors"
|
|
||||||
"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"
|
||||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
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)
|
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 {
|
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)
|
subject, err := authtypes.NewSubject(authtypes.TypeableUser, claims.UserID, orgID, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tupleSlice, err := typeable.Tuples(subject, relation, selectors, orgID)
|
tuples, err := typeable.Tuples(subject, relation, selectors, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tuples := make(map[string]*openfgav1.TupleKey, len(tupleSlice))
|
err = server.BatchCheck(ctx, tuples)
|
||||||
for idx, tuple := range tupleSlice {
|
|
||||||
tuples[strconv.Itoa(idx)] = tuple
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := server.BatchCheck(ctx, tuples)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, resp := range response {
|
return nil
|
||||||
if resp.Authorized {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "subjects are not authorized for requested access")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) CheckWithTupleCreationWithoutClaims(ctx context.Context, orgID valuer.UUID, relation authtypes.Relation, typeable authtypes.Typeable, selectors []authtypes.Selector, _ []authtypes.Selector) error {
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tupleSlice, err := typeable.Tuples(subject, relation, selectors, orgID)
|
tuples, err := typeable.Tuples(subject, relation, selectors, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tuples := make(map[string]*openfgav1.TupleKey, len(tupleSlice))
|
err = server.BatchCheck(ctx, tuples)
|
||||||
for idx, tuple := range tupleSlice {
|
|
||||||
tuples[strconv.Itoa(idx)] = tuple
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := server.BatchCheck(ctx, tuples)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, resp := range response {
|
return nil
|
||||||
if resp.Authorized {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "subjects are not authorized for requested access")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) BatchCheck(ctx context.Context, tupleReq map[string]*openfgav1.TupleKey) (map[string]*authtypes.TupleKeyAuthorization, error) {
|
func (server *Server) BatchCheck(ctx context.Context, tuples []*openfgav1.TupleKey) error {
|
||||||
return server.pkgAuthzService.BatchCheck(ctx, tupleReq)
|
return server.pkgAuthzService.BatchCheck(ctx, tuples)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) ListObjects(ctx context.Context, subject string, relation authtypes.Relation, typeable authtypes.Typeable) ([]*authtypes.Object, error) {
|
func (server *Server) ListObjects(ctx context.Context, subject string, relation authtypes.Relation, typeable authtypes.Typeable) ([]*authtypes.Object, error) {
|
||||||
|
|||||||
@@ -220,7 +220,6 @@ func (module *module) MustGetManagedRoleTransactions() map[string][]*authtypes.T
|
|||||||
return map[string][]*authtypes.Transaction{
|
return map[string][]*authtypes.Transaction{
|
||||||
roletypes.SigNozAnonymousRoleName: {
|
roletypes.SigNozAnonymousRoleName: {
|
||||||
{
|
{
|
||||||
ID: valuer.GenerateUUID(),
|
|
||||||
Relation: authtypes.RelationRead,
|
Relation: authtypes.RelationRead,
|
||||||
Object: *authtypes.MustNewObject(
|
Object: *authtypes.MustNewObject(
|
||||||
authtypes.Resource{
|
authtypes.Resource{
|
||||||
|
|||||||
@@ -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);
|
|
||||||
};
|
|
||||||
@@ -21,18 +21,11 @@ import { GeneratedAPIInstance } from '../../../index';
|
|||||||
import type {
|
import type {
|
||||||
CreateRole201,
|
CreateRole201,
|
||||||
DeleteRolePathParameters,
|
DeleteRolePathParameters,
|
||||||
GetObjects200,
|
|
||||||
GetObjectsPathParameters,
|
|
||||||
GetResources200,
|
|
||||||
GetRole200,
|
GetRole200,
|
||||||
GetRolePathParameters,
|
GetRolePathParameters,
|
||||||
ListRoles200,
|
ListRoles200,
|
||||||
PatchObjectsPathParameters,
|
|
||||||
PatchRolePathParameters,
|
PatchRolePathParameters,
|
||||||
RenderErrorResponseDTO,
|
RenderErrorResponseDTO,
|
||||||
RoletypesPatchableObjectsDTO,
|
|
||||||
RoletypesPatchableRoleDTO,
|
|
||||||
RoletypesPostableRoleDTO,
|
|
||||||
} from '../sigNoz.schemas';
|
} from '../sigNoz.schemas';
|
||||||
|
|
||||||
type AwaitedInput<T> = PromiseLike<T> | T;
|
type AwaitedInput<T> = PromiseLike<T> | T;
|
||||||
@@ -121,15 +114,10 @@ export const invalidateListRoles = async (
|
|||||||
* This endpoint creates a role
|
* This endpoint creates a role
|
||||||
* @summary Create role
|
* @summary Create role
|
||||||
*/
|
*/
|
||||||
export const createRole = (
|
export const createRole = (signal?: AbortSignal) => {
|
||||||
roletypesPostableRoleDTO: RoletypesPostableRoleDTO,
|
|
||||||
signal?: AbortSignal,
|
|
||||||
) => {
|
|
||||||
return GeneratedAPIInstance<CreateRole201>({
|
return GeneratedAPIInstance<CreateRole201>({
|
||||||
url: `/api/v1/roles`,
|
url: `/api/v1/roles`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
data: roletypesPostableRoleDTO,
|
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -141,13 +129,13 @@ export const getCreateRoleMutationOptions = <
|
|||||||
mutation?: UseMutationOptions<
|
mutation?: UseMutationOptions<
|
||||||
Awaited<ReturnType<typeof createRole>>,
|
Awaited<ReturnType<typeof createRole>>,
|
||||||
TError,
|
TError,
|
||||||
{ data: RoletypesPostableRoleDTO },
|
void,
|
||||||
TContext
|
TContext
|
||||||
>;
|
>;
|
||||||
}): UseMutationOptions<
|
}): UseMutationOptions<
|
||||||
Awaited<ReturnType<typeof createRole>>,
|
Awaited<ReturnType<typeof createRole>>,
|
||||||
TError,
|
TError,
|
||||||
{ data: RoletypesPostableRoleDTO },
|
void,
|
||||||
TContext
|
TContext
|
||||||
> => {
|
> => {
|
||||||
const mutationKey = ['createRole'];
|
const mutationKey = ['createRole'];
|
||||||
@@ -161,11 +149,9 @@ export const getCreateRoleMutationOptions = <
|
|||||||
|
|
||||||
const mutationFn: MutationFunction<
|
const mutationFn: MutationFunction<
|
||||||
Awaited<ReturnType<typeof createRole>>,
|
Awaited<ReturnType<typeof createRole>>,
|
||||||
{ data: RoletypesPostableRoleDTO }
|
void
|
||||||
> = (props) => {
|
> = () => {
|
||||||
const { data } = props ?? {};
|
return createRole();
|
||||||
|
|
||||||
return createRole(data);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return { mutationFn, ...mutationOptions };
|
return { mutationFn, ...mutationOptions };
|
||||||
@@ -174,7 +160,7 @@ export const getCreateRoleMutationOptions = <
|
|||||||
export type CreateRoleMutationResult = NonNullable<
|
export type CreateRoleMutationResult = NonNullable<
|
||||||
Awaited<ReturnType<typeof createRole>>
|
Awaited<ReturnType<typeof createRole>>
|
||||||
>;
|
>;
|
||||||
export type CreateRoleMutationBody = RoletypesPostableRoleDTO;
|
|
||||||
export type CreateRoleMutationError = RenderErrorResponseDTO;
|
export type CreateRoleMutationError = RenderErrorResponseDTO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -187,13 +173,13 @@ export const useCreateRole = <
|
|||||||
mutation?: UseMutationOptions<
|
mutation?: UseMutationOptions<
|
||||||
Awaited<ReturnType<typeof createRole>>,
|
Awaited<ReturnType<typeof createRole>>,
|
||||||
TError,
|
TError,
|
||||||
{ data: RoletypesPostableRoleDTO },
|
void,
|
||||||
TContext
|
TContext
|
||||||
>;
|
>;
|
||||||
}): UseMutationResult<
|
}): UseMutationResult<
|
||||||
Awaited<ReturnType<typeof createRole>>,
|
Awaited<ReturnType<typeof createRole>>,
|
||||||
TError,
|
TError,
|
||||||
{ data: RoletypesPostableRoleDTO },
|
void,
|
||||||
TContext
|
TContext
|
||||||
> => {
|
> => {
|
||||||
const mutationOptions = getCreateRoleMutationOptions(options);
|
const mutationOptions = getCreateRoleMutationOptions(options);
|
||||||
@@ -372,15 +358,10 @@ export const invalidateGetRole = async (
|
|||||||
* This endpoint patches a role
|
* This endpoint patches a role
|
||||||
* @summary Patch role
|
* @summary Patch role
|
||||||
*/
|
*/
|
||||||
export const patchRole = (
|
export const patchRole = ({ id }: PatchRolePathParameters) => {
|
||||||
{ id }: PatchRolePathParameters,
|
|
||||||
roletypesPatchableRoleDTO: RoletypesPatchableRoleDTO,
|
|
||||||
) => {
|
|
||||||
return GeneratedAPIInstance<string>({
|
return GeneratedAPIInstance<string>({
|
||||||
url: `/api/v1/roles/${id}`,
|
url: `/api/v1/roles/${id}`,
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
data: roletypesPatchableRoleDTO,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -391,13 +372,13 @@ export const getPatchRoleMutationOptions = <
|
|||||||
mutation?: UseMutationOptions<
|
mutation?: UseMutationOptions<
|
||||||
Awaited<ReturnType<typeof patchRole>>,
|
Awaited<ReturnType<typeof patchRole>>,
|
||||||
TError,
|
TError,
|
||||||
{ pathParams: PatchRolePathParameters; data: RoletypesPatchableRoleDTO },
|
{ pathParams: PatchRolePathParameters },
|
||||||
TContext
|
TContext
|
||||||
>;
|
>;
|
||||||
}): UseMutationOptions<
|
}): UseMutationOptions<
|
||||||
Awaited<ReturnType<typeof patchRole>>,
|
Awaited<ReturnType<typeof patchRole>>,
|
||||||
TError,
|
TError,
|
||||||
{ pathParams: PatchRolePathParameters; data: RoletypesPatchableRoleDTO },
|
{ pathParams: PatchRolePathParameters },
|
||||||
TContext
|
TContext
|
||||||
> => {
|
> => {
|
||||||
const mutationKey = ['patchRole'];
|
const mutationKey = ['patchRole'];
|
||||||
@@ -411,11 +392,11 @@ export const getPatchRoleMutationOptions = <
|
|||||||
|
|
||||||
const mutationFn: MutationFunction<
|
const mutationFn: MutationFunction<
|
||||||
Awaited<ReturnType<typeof patchRole>>,
|
Awaited<ReturnType<typeof patchRole>>,
|
||||||
{ pathParams: PatchRolePathParameters; data: RoletypesPatchableRoleDTO }
|
{ pathParams: PatchRolePathParameters }
|
||||||
> = (props) => {
|
> = (props) => {
|
||||||
const { pathParams, data } = props ?? {};
|
const { pathParams } = props ?? {};
|
||||||
|
|
||||||
return patchRole(pathParams, data);
|
return patchRole(pathParams);
|
||||||
};
|
};
|
||||||
|
|
||||||
return { mutationFn, ...mutationOptions };
|
return { mutationFn, ...mutationOptions };
|
||||||
@@ -424,7 +405,7 @@ export const getPatchRoleMutationOptions = <
|
|||||||
export type PatchRoleMutationResult = NonNullable<
|
export type PatchRoleMutationResult = NonNullable<
|
||||||
Awaited<ReturnType<typeof patchRole>>
|
Awaited<ReturnType<typeof patchRole>>
|
||||||
>;
|
>;
|
||||||
export type PatchRoleMutationBody = RoletypesPatchableRoleDTO;
|
|
||||||
export type PatchRoleMutationError = RenderErrorResponseDTO;
|
export type PatchRoleMutationError = RenderErrorResponseDTO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -437,292 +418,16 @@ export const usePatchRole = <
|
|||||||
mutation?: UseMutationOptions<
|
mutation?: UseMutationOptions<
|
||||||
Awaited<ReturnType<typeof patchRole>>,
|
Awaited<ReturnType<typeof patchRole>>,
|
||||||
TError,
|
TError,
|
||||||
{ pathParams: PatchRolePathParameters; data: RoletypesPatchableRoleDTO },
|
{ pathParams: PatchRolePathParameters },
|
||||||
TContext
|
TContext
|
||||||
>;
|
>;
|
||||||
}): UseMutationResult<
|
}): UseMutationResult<
|
||||||
Awaited<ReturnType<typeof patchRole>>,
|
Awaited<ReturnType<typeof patchRole>>,
|
||||||
TError,
|
TError,
|
||||||
{ pathParams: PatchRolePathParameters; data: RoletypesPatchableRoleDTO },
|
{ pathParams: PatchRolePathParameters },
|
||||||
TContext
|
TContext
|
||||||
> => {
|
> => {
|
||||||
const mutationOptions = getPatchRoleMutationOptions(options);
|
const mutationOptions = getPatchRoleMutationOptions(options);
|
||||||
|
|
||||||
return useMutation(mutationOptions);
|
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;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -127,18 +127,6 @@ export interface AuthtypesGettableTokenDTO {
|
|||||||
tokenType?: string;
|
tokenType?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthtypesGettableTransactionDTO {
|
|
||||||
/**
|
|
||||||
* @type boolean
|
|
||||||
*/
|
|
||||||
authorized: boolean;
|
|
||||||
object: AuthtypesObjectDTO;
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
relation: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AuthtypesGoogleConfigDTODomainToAdminEmail = {
|
export type AuthtypesGoogleConfigDTODomainToAdminEmail = {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
};
|
};
|
||||||
@@ -182,10 +170,6 @@ export interface AuthtypesGoogleConfigDTO {
|
|||||||
serviceAccountJson?: string;
|
serviceAccountJson?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthtypesNameDTO {
|
|
||||||
[key: string]: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AuthtypesOIDCConfigDTO {
|
export interface AuthtypesOIDCConfigDTO {
|
||||||
claimMapping?: AuthtypesAttributeMappingDTO;
|
claimMapping?: AuthtypesAttributeMappingDTO;
|
||||||
/**
|
/**
|
||||||
@@ -214,11 +198,6 @@ export interface AuthtypesOIDCConfigDTO {
|
|||||||
issuerAlias?: string;
|
issuerAlias?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthtypesObjectDTO {
|
|
||||||
resource: AuthtypesResourceDTO;
|
|
||||||
selector: AuthtypesSelectorDTO;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AuthtypesOrgSessionContextDTO {
|
export interface AuthtypesOrgSessionContextDTO {
|
||||||
authNSupport?: AuthtypesAuthNSupportDTO;
|
authNSupport?: AuthtypesAuthNSupportDTO;
|
||||||
/**
|
/**
|
||||||
@@ -269,14 +248,6 @@ export interface AuthtypesPostableRotateTokenDTO {
|
|||||||
refreshToken?: string;
|
refreshToken?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthtypesResourceDTO {
|
|
||||||
name: AuthtypesNameDTO;
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @nullable
|
* @nullable
|
||||||
*/
|
*/
|
||||||
@@ -320,10 +291,6 @@ export interface AuthtypesSamlConfigDTO {
|
|||||||
samlIdp?: string;
|
samlIdp?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthtypesSelectorDTO {
|
|
||||||
[key: string]: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AuthtypesSessionContextDTO {
|
export interface AuthtypesSessionContextDTO {
|
||||||
/**
|
/**
|
||||||
* @type boolean
|
* @type boolean
|
||||||
@@ -336,18 +303,6 @@ export interface AuthtypesSessionContextDTO {
|
|||||||
orgs?: AuthtypesOrgSessionContextDTO[] | null;
|
orgs?: AuthtypesOrgSessionContextDTO[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthtypesTransactionDTO {
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
id?: string;
|
|
||||||
object: AuthtypesObjectDTO;
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
relation: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AuthtypesUpdateableAuthDomainDTO {
|
export interface AuthtypesUpdateableAuthDomainDTO {
|
||||||
config?: AuthtypesAuthDomainConfigDTO;
|
config?: AuthtypesAuthDomainConfigDTO;
|
||||||
}
|
}
|
||||||
@@ -1992,57 +1947,6 @@ export interface RenderErrorResponseDTO {
|
|||||||
status?: string;
|
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 {
|
export interface RoletypesRoleDTO {
|
||||||
/**
|
/**
|
||||||
* @type string
|
* @type string
|
||||||
@@ -2052,7 +1956,7 @@ export interface RoletypesRoleDTO {
|
|||||||
/**
|
/**
|
||||||
* @type string
|
* @type string
|
||||||
*/
|
*/
|
||||||
description: string;
|
description?: string;
|
||||||
/**
|
/**
|
||||||
* @type string
|
* @type string
|
||||||
*/
|
*/
|
||||||
@@ -2060,15 +1964,15 @@ export interface RoletypesRoleDTO {
|
|||||||
/**
|
/**
|
||||||
* @type string
|
* @type string
|
||||||
*/
|
*/
|
||||||
name: string;
|
name?: string;
|
||||||
/**
|
/**
|
||||||
* @type string
|
* @type string
|
||||||
*/
|
*/
|
||||||
orgId: string;
|
orgId?: string;
|
||||||
/**
|
/**
|
||||||
* @type string
|
* @type string
|
||||||
*/
|
*/
|
||||||
type: string;
|
type?: string;
|
||||||
/**
|
/**
|
||||||
* @type string
|
* @type string
|
||||||
* @format date-time
|
* @format date-time
|
||||||
@@ -2595,17 +2499,6 @@ export interface ZeustypesPostableProfileDTO {
|
|||||||
where_did_you_discover_signoz: string;
|
where_did_you_discover_signoz: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AuthzCheck200 = {
|
|
||||||
/**
|
|
||||||
* @type array
|
|
||||||
*/
|
|
||||||
data?: AuthtypesGettableTransactionDTO[];
|
|
||||||
/**
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
status?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ChangePasswordPathParameters = {
|
export type ChangePasswordPathParameters = {
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
@@ -3009,33 +2902,6 @@ export type GetRole200 = {
|
|||||||
export type PatchRolePathParameters = {
|
export type PatchRolePathParameters = {
|
||||||
id: string;
|
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 = {
|
export type ListUsers200 = {
|
||||||
/**
|
/**
|
||||||
* @type array
|
* @type array
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
|||||||
|
|
||||||
import { FeatureKeys } from '../../../constants/features';
|
import { FeatureKeys } from '../../../constants/features';
|
||||||
import { useAppContext } from '../../../providers/App/App';
|
import { useAppContext } from '../../../providers/App/App';
|
||||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
import { getOrderByFromParams } from '../commonUtils';
|
||||||
import {
|
import {
|
||||||
GetK8sEntityToAggregateAttribute,
|
GetK8sEntityToAggregateAttribute,
|
||||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||||
@@ -103,10 +103,9 @@ function K8sClustersList({
|
|||||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||||
if (groupBy) {
|
if (groupBy) {
|
||||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
const decoded = decodeURIComponent(groupBy);
|
||||||
if (parsed) {
|
const parsed = JSON.parse(decoded);
|
||||||
return parsed;
|
return parsed as IBuilderQuery['groupBy'];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
|||||||
|
|
||||||
import { FeatureKeys } from '../../../constants/features';
|
import { FeatureKeys } from '../../../constants/features';
|
||||||
import { useAppContext } from '../../../providers/App/App';
|
import { useAppContext } from '../../../providers/App/App';
|
||||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
import { getOrderByFromParams } from '../commonUtils';
|
||||||
import {
|
import {
|
||||||
GetK8sEntityToAggregateAttribute,
|
GetK8sEntityToAggregateAttribute,
|
||||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||||
@@ -105,10 +105,9 @@ function K8sDaemonSetsList({
|
|||||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||||
if (groupBy) {
|
if (groupBy) {
|
||||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
const decoded = decodeURIComponent(groupBy);
|
||||||
if (parsed) {
|
const parsed = JSON.parse(decoded);
|
||||||
return parsed;
|
return parsed as IBuilderQuery['groupBy'];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
|||||||
|
|
||||||
import { FeatureKeys } from '../../../constants/features';
|
import { FeatureKeys } from '../../../constants/features';
|
||||||
import { useAppContext } from '../../../providers/App/App';
|
import { useAppContext } from '../../../providers/App/App';
|
||||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
import { getOrderByFromParams } from '../commonUtils';
|
||||||
import {
|
import {
|
||||||
GetK8sEntityToAggregateAttribute,
|
GetK8sEntityToAggregateAttribute,
|
||||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||||
@@ -106,10 +106,9 @@ function K8sDeploymentsList({
|
|||||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||||
if (groupBy) {
|
if (groupBy) {
|
||||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
const decoded = decodeURIComponent(groupBy);
|
||||||
if (parsed) {
|
const parsed = JSON.parse(decoded);
|
||||||
return parsed;
|
return parsed as IBuilderQuery['groupBy'];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
|||||||
|
|
||||||
import { FeatureKeys } from '../../../constants/features';
|
import { FeatureKeys } from '../../../constants/features';
|
||||||
import { useAppContext } from '../../../providers/App/App';
|
import { useAppContext } from '../../../providers/App/App';
|
||||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
import { getOrderByFromParams } from '../commonUtils';
|
||||||
import {
|
import {
|
||||||
GetK8sEntityToAggregateAttribute,
|
GetK8sEntityToAggregateAttribute,
|
||||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||||
@@ -101,10 +101,9 @@ function K8sJobsList({
|
|||||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||||
if (groupBy) {
|
if (groupBy) {
|
||||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
const decoded = decodeURIComponent(groupBy);
|
||||||
if (parsed) {
|
const parsed = JSON.parse(decoded);
|
||||||
return parsed;
|
return parsed as IBuilderQuery['groupBy'];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteRe
|
|||||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
import { safeParseJSON } from './commonUtils';
|
|
||||||
import { INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory } from './constants';
|
import { INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory } from './constants';
|
||||||
import K8sFiltersSidePanel from './K8sFiltersSidePanel/K8sFiltersSidePanel';
|
import K8sFiltersSidePanel from './K8sFiltersSidePanel/K8sFiltersSidePanel';
|
||||||
import { IEntityColumn } from './utils';
|
import { IEntityColumn } from './utils';
|
||||||
@@ -59,10 +58,9 @@ function K8sHeader({
|
|||||||
const urlFilters = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.FILTERS);
|
const urlFilters = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.FILTERS);
|
||||||
let { filters } = currentQuery.builder.queryData[0];
|
let { filters } = currentQuery.builder.queryData[0];
|
||||||
if (urlFilters) {
|
if (urlFilters) {
|
||||||
const parsed = safeParseJSON<IBuilderQuery['filters']>(urlFilters);
|
const decoded = decodeURIComponent(urlFilters);
|
||||||
if (parsed) {
|
const parsed = JSON.parse(decoded);
|
||||||
filters = parsed;
|
filters = parsed;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...currentQuery,
|
...currentQuery,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
|||||||
|
|
||||||
import { FeatureKeys } from '../../../constants/features';
|
import { FeatureKeys } from '../../../constants/features';
|
||||||
import { useAppContext } from '../../../providers/App/App';
|
import { useAppContext } from '../../../providers/App/App';
|
||||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
import { getOrderByFromParams } from '../commonUtils';
|
||||||
import {
|
import {
|
||||||
GetK8sEntityToAggregateAttribute,
|
GetK8sEntityToAggregateAttribute,
|
||||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||||
@@ -104,10 +104,9 @@ function K8sNamespacesList({
|
|||||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||||
if (groupBy) {
|
if (groupBy) {
|
||||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
const decoded = decodeURIComponent(groupBy);
|
||||||
if (parsed) {
|
const parsed = JSON.parse(decoded);
|
||||||
return parsed;
|
return parsed as IBuilderQuery['groupBy'];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
|||||||
|
|
||||||
import { FeatureKeys } from '../../../constants/features';
|
import { FeatureKeys } from '../../../constants/features';
|
||||||
import { useAppContext } from '../../../providers/App/App';
|
import { useAppContext } from '../../../providers/App/App';
|
||||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
import { getOrderByFromParams } from '../commonUtils';
|
||||||
import {
|
import {
|
||||||
GetK8sEntityToAggregateAttribute,
|
GetK8sEntityToAggregateAttribute,
|
||||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||||
@@ -99,10 +99,9 @@ function K8sNodesList({
|
|||||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||||
if (groupBy) {
|
if (groupBy) {
|
||||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
const decoded = decodeURIComponent(groupBy);
|
||||||
if (parsed) {
|
const parsed = JSON.parse(decoded);
|
||||||
return parsed;
|
return parsed as IBuilderQuery['groupBy'];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
|||||||
|
|
||||||
import { FeatureKeys } from '../../../constants/features';
|
import { FeatureKeys } from '../../../constants/features';
|
||||||
import { useAppContext } from '../../../providers/App/App';
|
import { useAppContext } from '../../../providers/App/App';
|
||||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
import { getOrderByFromParams } from '../commonUtils';
|
||||||
import {
|
import {
|
||||||
GetK8sEntityToAggregateAttribute,
|
GetK8sEntityToAggregateAttribute,
|
||||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||||
@@ -92,10 +92,9 @@ function K8sPodsList({
|
|||||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||||
if (groupBy) {
|
if (groupBy) {
|
||||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
const decoded = decodeURIComponent(groupBy);
|
||||||
if (parsed) {
|
const parsed = JSON.parse(decoded);
|
||||||
return parsed;
|
return parsed as IBuilderQuery['groupBy'];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
|||||||
|
|
||||||
import { FeatureKeys } from '../../../constants/features';
|
import { FeatureKeys } from '../../../constants/features';
|
||||||
import { useAppContext } from '../../../providers/App/App';
|
import { useAppContext } from '../../../providers/App/App';
|
||||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
import { getOrderByFromParams } from '../commonUtils';
|
||||||
import {
|
import {
|
||||||
GetK8sEntityToAggregateAttribute,
|
GetK8sEntityToAggregateAttribute,
|
||||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||||
@@ -105,10 +105,9 @@ function K8sStatefulSetsList({
|
|||||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||||
if (groupBy) {
|
if (groupBy) {
|
||||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
const decoded = decodeURIComponent(groupBy);
|
||||||
if (parsed) {
|
const parsed = JSON.parse(decoded);
|
||||||
return parsed;
|
return parsed as IBuilderQuery['groupBy'];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
|||||||
|
|
||||||
import { FeatureKeys } from '../../../constants/features';
|
import { FeatureKeys } from '../../../constants/features';
|
||||||
import { useAppContext } from '../../../providers/App/App';
|
import { useAppContext } from '../../../providers/App/App';
|
||||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
import { getOrderByFromParams } from '../commonUtils';
|
||||||
import {
|
import {
|
||||||
GetK8sEntityToAggregateAttribute,
|
GetK8sEntityToAggregateAttribute,
|
||||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||||
@@ -105,10 +105,9 @@ function K8sVolumesList({
|
|||||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||||
if (groupBy) {
|
if (groupBy) {
|
||||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
const decoded = decodeURIComponent(groupBy);
|
||||||
if (parsed) {
|
const parsed = JSON.parse(decoded);
|
||||||
return parsed;
|
return parsed as IBuilderQuery['groupBy'];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
/* eslint-disable prefer-destructuring */
|
/* eslint-disable prefer-destructuring */
|
||||||
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import * as Sentry from '@sentry/react';
|
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { Table, Tooltip, Typography } from 'antd';
|
import { Table, Tooltip, Typography } from 'antd';
|
||||||
import { Progress } from 'antd/lib';
|
import { Progress } from 'antd/lib';
|
||||||
@@ -261,27 +260,6 @@ export const filterDuplicateFilters = (
|
|||||||
return uniqueFilters;
|
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 = (
|
export const getOrderByFromParams = (
|
||||||
searchParams: URLSearchParams,
|
searchParams: URLSearchParams,
|
||||||
returnNullAsDefault = false,
|
returnNullAsDefault = false,
|
||||||
@@ -293,12 +271,9 @@ export const getOrderByFromParams = (
|
|||||||
INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY,
|
INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY,
|
||||||
);
|
);
|
||||||
if (orderByFromParams) {
|
if (orderByFromParams) {
|
||||||
const parsed = safeParseJSON<{ columnName: string; order: 'asc' | 'desc' }>(
|
const decoded = decodeURIComponent(orderByFromParams);
|
||||||
orderByFromParams,
|
const parsed = JSON.parse(decoded);
|
||||||
);
|
return parsed as { columnName: string; order: 'asc' | 'desc' };
|
||||||
if (parsed) {
|
|
||||||
return parsed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (returnNullAsDefault) {
|
if (returnNullAsDefault) {
|
||||||
return null;
|
return null;
|
||||||
@@ -312,7 +287,13 @@ export const getFiltersFromParams = (
|
|||||||
): IBuilderQuery['filters'] | null => {
|
): IBuilderQuery['filters'] | null => {
|
||||||
const filtersFromParams = searchParams.get(queryKey);
|
const filtersFromParams = searchParams.get(queryKey);
|
||||||
if (filtersFromParams) {
|
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;
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -88,7 +88,10 @@ function InviteTeamMembers({
|
|||||||
setTeamMembersToInvite((prev) => (prev || []).filter((m) => m.id !== id));
|
setTeamMembersToInvite((prev) => (prev || []).filter((m) => m.id !== id));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Validation function to check all users
|
const isMemberTouched = (member: TeamMember): boolean =>
|
||||||
|
member.email.trim() !== '' ||
|
||||||
|
Boolean(member.role && member.role.trim() !== '');
|
||||||
|
|
||||||
const validateAllUsers = (): boolean => {
|
const validateAllUsers = (): boolean => {
|
||||||
let isValid = true;
|
let isValid = true;
|
||||||
let hasEmailErrors = false;
|
let hasEmailErrors = false;
|
||||||
@@ -96,7 +99,9 @@ function InviteTeamMembers({
|
|||||||
|
|
||||||
const updatedEmailValidity: Record<string, boolean> = {};
|
const updatedEmailValidity: Record<string, boolean> = {};
|
||||||
|
|
||||||
teamMembersToInvite?.forEach((member) => {
|
const touchedMembers = teamMembersToInvite?.filter(isMemberTouched) ?? [];
|
||||||
|
|
||||||
|
touchedMembers?.forEach((member) => {
|
||||||
const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(member.email);
|
const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(member.email);
|
||||||
const roleValid = Boolean(member.role && member.role.trim() !== '');
|
const roleValid = Boolean(member.role && member.role.trim() !== '');
|
||||||
|
|
||||||
@@ -150,12 +155,12 @@ function InviteTeamMembers({
|
|||||||
|
|
||||||
const handleNext = (): void => {
|
const handleNext = (): void => {
|
||||||
if (validateAllUsers()) {
|
if (validateAllUsers()) {
|
||||||
setTeamMembers(teamMembersToInvite || []);
|
setTeamMembers(teamMembersToInvite?.filter(isMemberTouched) ?? []);
|
||||||
setHasInvalidEmails(false);
|
setHasInvalidEmails(false);
|
||||||
setHasInvalidRoles(false);
|
setHasInvalidRoles(false);
|
||||||
setInviteError(null);
|
setInviteError(null);
|
||||||
sendInvites({
|
sendInvites({
|
||||||
invites: teamMembersToInvite || [],
|
invites: teamMembersToInvite?.filter(isMemberTouched) ?? [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -230,12 +235,12 @@ function InviteTeamMembers({
|
|||||||
|
|
||||||
const getValidationErrorMessage = (): string => {
|
const getValidationErrorMessage = (): string => {
|
||||||
if (hasInvalidEmails && hasInvalidRoles) {
|
if (hasInvalidEmails && hasInvalidRoles) {
|
||||||
return 'Please enter valid emails and select roles for all team members';
|
return 'Please enter valid emails and select roles for team members';
|
||||||
}
|
}
|
||||||
if (hasInvalidEmails) {
|
if (hasInvalidEmails) {
|
||||||
return 'Please enter valid emails for all team members';
|
return 'Please enter valid emails for team members';
|
||||||
}
|
}
|
||||||
return 'Please select roles for all team members';
|
return 'Please select roles for team members';
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDoLater = (): void => {
|
const handleDoLater = (): void => {
|
||||||
|
|||||||
@@ -0,0 +1,485 @@
|
|||||||
|
import { rest, server } from 'mocks-server/server';
|
||||||
|
import {
|
||||||
|
fireEvent,
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
userEvent,
|
||||||
|
waitFor,
|
||||||
|
} from 'tests/test-utils';
|
||||||
|
|
||||||
|
import InviteTeamMembers from '../InviteTeamMembers';
|
||||||
|
|
||||||
|
jest.mock('api/common/logEvent', () => ({
|
||||||
|
__esModule: true,
|
||||||
|
default: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mockNotificationSuccess = jest.fn() as jest.MockedFunction<
|
||||||
|
(args: { message: string }) => void
|
||||||
|
>;
|
||||||
|
const mockNotificationError = jest.fn() as jest.MockedFunction<
|
||||||
|
(args: { message: string }) => void
|
||||||
|
>;
|
||||||
|
|
||||||
|
jest.mock('hooks/useNotifications', () => ({
|
||||||
|
useNotifications: (): any => ({
|
||||||
|
notifications: {
|
||||||
|
success: mockNotificationSuccess,
|
||||||
|
error: mockNotificationError,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const INVITE_USERS_ENDPOINT = '*/api/v1/invite/bulk';
|
||||||
|
|
||||||
|
interface TeamMember {
|
||||||
|
email: string;
|
||||||
|
role: string;
|
||||||
|
name: string;
|
||||||
|
frontendBaseUrl: string;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InviteRequestBody {
|
||||||
|
invites: { email: string; role: string }[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RenderProps {
|
||||||
|
isLoading?: boolean;
|
||||||
|
teamMembers?: TeamMember[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockOnNext = jest.fn() as jest.MockedFunction<() => void>;
|
||||||
|
const mockSetTeamMembers = jest.fn() as jest.MockedFunction<
|
||||||
|
(members: TeamMember[]) => void
|
||||||
|
>;
|
||||||
|
|
||||||
|
function renderComponent({
|
||||||
|
isLoading = false,
|
||||||
|
teamMembers = null,
|
||||||
|
}: RenderProps = {}): ReturnType<typeof render> {
|
||||||
|
return render(
|
||||||
|
<InviteTeamMembers
|
||||||
|
isLoading={isLoading}
|
||||||
|
teamMembers={teamMembers}
|
||||||
|
setTeamMembers={mockSetTeamMembers}
|
||||||
|
onNext={mockOnNext}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectRole(
|
||||||
|
user: ReturnType<typeof userEvent.setup>,
|
||||||
|
selectIndex: number,
|
||||||
|
optionLabel: string,
|
||||||
|
): Promise<void> {
|
||||||
|
const placeholders = screen.getAllByText(/select roles/i);
|
||||||
|
await user.click(placeholders[selectIndex]);
|
||||||
|
const optionContent = await screen.findByText(optionLabel);
|
||||||
|
fireEvent.click(optionContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('InviteTeamMembers', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
|
||||||
|
server.use(
|
||||||
|
rest.post(INVITE_USERS_ENDPOINT, (_, res, ctx) =>
|
||||||
|
res(ctx.status(200), ctx.json({ status: 'success' })),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.useRealTimers();
|
||||||
|
server.resetHandlers();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Initial rendering', () => {
|
||||||
|
it('renders the page header, column labels, default rows, and action buttons', () => {
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getByRole('heading', { name: /invite your team/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByText(/signoz is a lot more useful with collaborators/i),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getAllByPlaceholderText(/e\.g\. john@signoz\.io/i),
|
||||||
|
).toHaveLength(3);
|
||||||
|
expect(screen.getByText('Email address')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Roles')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByRole('button', { name: /complete/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByRole('button', { name: /i'll do this later/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('disables both action buttons while isLoading is true', () => {
|
||||||
|
renderComponent({ isLoading: true });
|
||||||
|
|
||||||
|
expect(screen.getByRole('button', { name: /complete/i })).toBeDisabled();
|
||||||
|
expect(
|
||||||
|
screen.getByRole('button', { name: /i'll do this later/i }),
|
||||||
|
).toBeDisabled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Row management', () => {
|
||||||
|
it('adds a new empty row when "Add another" is clicked', async () => {
|
||||||
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getAllByPlaceholderText(/e\.g\. john@signoz\.io/i),
|
||||||
|
).toHaveLength(3);
|
||||||
|
|
||||||
|
await user.click(screen.getByRole('button', { name: /add another/i }));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getAllByPlaceholderText(/e\.g\. john@signoz\.io/i),
|
||||||
|
).toHaveLength(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removes the correct row when its trash icon is clicked', async () => {
|
||||||
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
const emailInputs = screen.getAllByPlaceholderText(
|
||||||
|
/e\.g\. john@signoz\.io/i,
|
||||||
|
);
|
||||||
|
await user.type(emailInputs[0], 'first@example.com');
|
||||||
|
await screen.findByDisplayValue('first@example.com');
|
||||||
|
|
||||||
|
await user.click(
|
||||||
|
screen.getAllByRole('button', { name: /remove team member/i })[0],
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.queryByDisplayValue('first@example.com'),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getAllByPlaceholderText(/e\.g\. john@signoz\.io/i),
|
||||||
|
).toHaveLength(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hides remove buttons when only one row remains', async () => {
|
||||||
|
renderComponent();
|
||||||
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
|
|
||||||
|
let removeButtons = screen.getAllByRole('button', {
|
||||||
|
name: /remove team member/i,
|
||||||
|
});
|
||||||
|
while (removeButtons.length > 0) {
|
||||||
|
await user.click(removeButtons[0]);
|
||||||
|
removeButtons = screen.queryAllByRole('button', {
|
||||||
|
name: /remove team member/i,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.queryByRole('button', { name: /remove team member/i }),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Inline email validation', () => {
|
||||||
|
it('shows an inline error after typing an invalid email and clears it when a valid email is entered', async () => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
const user = userEvent.setup({
|
||||||
|
advanceTimers: (ms) => jest.advanceTimersByTime(ms),
|
||||||
|
});
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
const [firstInput] = screen.getAllByPlaceholderText(
|
||||||
|
/e\.g\. john@signoz\.io/i,
|
||||||
|
);
|
||||||
|
|
||||||
|
await user.type(firstInput, 'not-an-email');
|
||||||
|
jest.advanceTimersByTime(600);
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText(/invalid email address/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.clear(firstInput);
|
||||||
|
await user.type(firstInput, 'good@example.com');
|
||||||
|
jest.advanceTimersByTime(600);
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/invalid email address/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not show an inline error when the field is cleared back to empty', async () => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
const user = userEvent.setup({
|
||||||
|
advanceTimers: (ms) => jest.advanceTimersByTime(ms),
|
||||||
|
});
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
const [firstInput] = screen.getAllByPlaceholderText(
|
||||||
|
/e\.g\. john@signoz\.io/i,
|
||||||
|
);
|
||||||
|
await user.type(firstInput, 'a');
|
||||||
|
await user.clear(firstInput);
|
||||||
|
jest.advanceTimersByTime(600);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/invalid email address/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Validation callout on Complete', () => {
|
||||||
|
it('shows the correct callout message for each combination of email/role validity', async () => {
|
||||||
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
const removeButtons = screen.getAllByRole('button', {
|
||||||
|
name: /remove team member/i,
|
||||||
|
});
|
||||||
|
await user.click(removeButtons[0]);
|
||||||
|
await user.click(
|
||||||
|
screen.getAllByRole('button', { name: /remove team member/i })[0],
|
||||||
|
);
|
||||||
|
|
||||||
|
const [firstInput] = screen.getAllByPlaceholderText(
|
||||||
|
/e\.g\. john@signoz\.io/i,
|
||||||
|
);
|
||||||
|
|
||||||
|
await user.type(firstInput, 'bad-email');
|
||||||
|
await user.click(screen.getByRole('button', { name: /complete/i }));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.getByText(
|
||||||
|
/please enter valid emails and select roles for team members/i,
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/please enter valid emails for team members/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/please select roles for team members/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
await selectRole(user, 0, 'Viewer');
|
||||||
|
await user.click(screen.getByRole('button', { name: /complete/i }));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.getByText(/please enter valid emails for team members/i),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/please select roles for team members/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/please enter valid emails and select roles/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.clear(firstInput);
|
||||||
|
await user.type(firstInput, 'valid@example.com');
|
||||||
|
await user.click(screen.getByRole('button', { name: /add another/i }));
|
||||||
|
const allInputs = screen.getAllByPlaceholderText(/e\.g\. john@signoz\.io/i);
|
||||||
|
await user.type(allInputs[1], 'norole@example.com');
|
||||||
|
await user.click(screen.getByRole('button', { name: /complete/i }));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.getByText(/please select roles for team members/i),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/please enter valid emails for team members/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/please enter valid emails and select roles/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('treats whitespace as untouched, clears the callout on fix-and-resubmit, and clears role error on role select', async () => {
|
||||||
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
const removeButtons = screen.getAllByRole('button', {
|
||||||
|
name: /remove team member/i,
|
||||||
|
});
|
||||||
|
await user.click(removeButtons[0]);
|
||||||
|
await user.click(
|
||||||
|
screen.getAllByRole('button', { name: /remove team member/i })[0],
|
||||||
|
);
|
||||||
|
|
||||||
|
const [firstInput] = screen.getAllByPlaceholderText(
|
||||||
|
/e\.g\. john@signoz\.io/i,
|
||||||
|
);
|
||||||
|
|
||||||
|
await user.type(firstInput, ' ');
|
||||||
|
await user.click(screen.getByRole('button', { name: /complete/i }));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/please enter valid emails/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByText(/please select roles/i)).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.clear(firstInput);
|
||||||
|
await user.type(firstInput, 'bad-email');
|
||||||
|
await user.click(screen.getByRole('button', { name: /complete/i }));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.getByText(
|
||||||
|
/please enter valid emails and select roles for team members/i,
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/please enter valid emails for team members/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/please select roles for team members/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.clear(firstInput);
|
||||||
|
await user.type(firstInput, 'good@example.com');
|
||||||
|
await selectRole(user, 0, 'Admin');
|
||||||
|
await user.click(screen.getByRole('button', { name: /complete/i }));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/please enter valid emails and select roles/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/please enter valid emails for team members/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/please select roles for team members/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => expect(mockOnNext).toHaveBeenCalledTimes(1), {
|
||||||
|
timeout: 1200,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not show a validation callout when all rows are untouched (empty)', async () => {
|
||||||
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
await user.click(screen.getByRole('button', { name: /complete/i }));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/please enter valid emails/i),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByText(/please select roles/i)).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(
|
||||||
|
() => {
|
||||||
|
expect(mockOnNext).toHaveBeenCalled();
|
||||||
|
},
|
||||||
|
{ timeout: 1200 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('API integration', () => {
|
||||||
|
it('only sends touched (non-empty) rows — empty rows are excluded from the invite payload', async () => {
|
||||||
|
let capturedBody: InviteRequestBody | null = null;
|
||||||
|
|
||||||
|
server.use(
|
||||||
|
rest.post(INVITE_USERS_ENDPOINT, async (req, res, ctx) => {
|
||||||
|
capturedBody = await req.json<InviteRequestBody>();
|
||||||
|
return res(ctx.status(200), ctx.json({ status: 'success' }));
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
const [firstInput] = screen.getAllByPlaceholderText(
|
||||||
|
/e\.g\. john@signoz\.io/i,
|
||||||
|
);
|
||||||
|
await user.type(firstInput, 'only@example.com');
|
||||||
|
await selectRole(user, 0, 'Admin');
|
||||||
|
await user.click(screen.getByRole('button', { name: /complete/i }));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(capturedBody).not.toBeNull();
|
||||||
|
expect(capturedBody?.invites).toHaveLength(1);
|
||||||
|
expect(capturedBody?.invites[0]).toMatchObject({
|
||||||
|
email: 'only@example.com',
|
||||||
|
role: 'ADMIN',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await waitFor(() => expect(mockOnNext).toHaveBeenCalled(), {
|
||||||
|
timeout: 1200,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls the invite API, shows a success notification, and calls onNext after the 1 s delay', async () => {
|
||||||
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
const [firstInput] = screen.getAllByPlaceholderText(
|
||||||
|
/e\.g\. john@signoz\.io/i,
|
||||||
|
);
|
||||||
|
await user.type(firstInput, 'alice@example.com');
|
||||||
|
await selectRole(user, 0, 'Admin');
|
||||||
|
await user.click(screen.getByRole('button', { name: /complete/i }));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockNotificationSuccess).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ message: 'Invites sent successfully!' }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(
|
||||||
|
() => {
|
||||||
|
expect(mockOnNext).toHaveBeenCalledTimes(1);
|
||||||
|
},
|
||||||
|
{ timeout: 1200 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders an API error container when the invite request fails', async () => {
|
||||||
|
server.use(
|
||||||
|
rest.post(INVITE_USERS_ENDPOINT, (_, res, ctx) =>
|
||||||
|
res(
|
||||||
|
ctx.status(500),
|
||||||
|
ctx.json({
|
||||||
|
errors: [{ code: 'INTERNAL_ERROR', msg: 'Something went wrong' }],
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
const [firstInput] = screen.getAllByPlaceholderText(
|
||||||
|
/e\.g\. john@signoz\.io/i,
|
||||||
|
);
|
||||||
|
await user.type(firstInput, 'fail@example.com');
|
||||||
|
await selectRole(user, 0, 'Viewer');
|
||||||
|
await user.click(screen.getByRole('button', { name: /complete/i }));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(document.querySelector('.auth-error-container')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.type(firstInput, 'x');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
document.querySelector('.auth-error-container'),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -207,10 +207,6 @@ func (provider *provider) AddToRouter(router *mux.Router) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := provider.addAuthzRoutes(router); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := provider.addFieldsRoutes(router); err != nil {
|
if err := provider.addFieldsRoutes(router); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/http/handler"
|
"github.com/SigNoz/signoz/pkg/http/handler"
|
||||||
"github.com/SigNoz/signoz/pkg/types"
|
"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/types/roletypes"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
@@ -16,12 +15,12 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
|||||||
Tags: []string{"role"},
|
Tags: []string{"role"},
|
||||||
Summary: "Create role",
|
Summary: "Create role",
|
||||||
Description: "This endpoint creates a role",
|
Description: "This endpoint creates a role",
|
||||||
Request: new(roletypes.PostableRole),
|
Request: nil,
|
||||||
RequestContentType: "",
|
RequestContentType: "",
|
||||||
Response: new(types.Identifiable),
|
Response: new(types.Identifiable),
|
||||||
ResponseContentType: "application/json",
|
ResponseContentType: "application/json",
|
||||||
SuccessStatusCode: http.StatusCreated,
|
SuccessStatusCode: http.StatusCreated,
|
||||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
|
ErrorStatusCodes: []int{},
|
||||||
Deprecated: false,
|
Deprecated: false,
|
||||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
})).Methods(http.MethodPost).GetError(); err != nil {
|
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||||
@@ -45,23 +44,6 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
|||||||
return err
|
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{
|
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authZ.AdminAccess(provider.authzHandler.Get), handler.OpenAPIDef{
|
||||||
ID: "GetRole",
|
ID: "GetRole",
|
||||||
Tags: []string{"role"},
|
Tags: []string{"role"},
|
||||||
@@ -79,51 +61,17 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
|||||||
return err
|
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{
|
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authZ.AdminAccess(provider.authzHandler.Patch), handler.OpenAPIDef{
|
||||||
ID: "PatchRole",
|
ID: "PatchRole",
|
||||||
Tags: []string{"role"},
|
Tags: []string{"role"},
|
||||||
Summary: "Patch role",
|
Summary: "Patch role",
|
||||||
Description: "This endpoint patches a role",
|
Description: "This endpoint patches a role",
|
||||||
Request: new(roletypes.PatchableRole),
|
Request: nil,
|
||||||
RequestContentType: "",
|
RequestContentType: "",
|
||||||
Response: nil,
|
Response: nil,
|
||||||
ResponseContentType: "application/json",
|
ResponseContentType: "application/json",
|
||||||
SuccessStatusCode: http.StatusNoContent,
|
SuccessStatusCode: http.StatusNoContent,
|
||||||
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
|
ErrorStatusCodes: []int{},
|
||||||
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},
|
|
||||||
Deprecated: false,
|
Deprecated: false,
|
||||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
})).Methods(http.MethodPatch).GetError(); err != nil {
|
})).Methods(http.MethodPatch).GetError(); err != nil {
|
||||||
@@ -140,7 +88,7 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
|||||||
Response: nil,
|
Response: nil,
|
||||||
ResponseContentType: "application/json",
|
ResponseContentType: "application/json",
|
||||||
SuccessStatusCode: http.StatusNoContent,
|
SuccessStatusCode: http.StatusNoContent,
|
||||||
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
|
ErrorStatusCodes: []int{},
|
||||||
Deprecated: false,
|
Deprecated: false,
|
||||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||||
})).Methods(http.MethodDelete).GetError(); err != nil {
|
})).Methods(http.MethodDelete).GetError(); err != nil {
|
||||||
|
|||||||
@@ -14,14 +14,17 @@ import (
|
|||||||
type AuthZ interface {
|
type AuthZ interface {
|
||||||
factory.Service
|
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 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
|
CheckWithTupleCreation(context.Context, authtypes.Claims, valuer.UUID, authtypes.Relation, authtypes.Typeable, []authtypes.Selector, []authtypes.Selector) error
|
||||||
|
|
||||||
// CheckWithTupleCreationWithoutClaims checks permissions for anonymous users.
|
// CheckWithTupleCreationWithoutClaims checks permissions for anonymous users.
|
||||||
CheckWithTupleCreationWithoutClaims(context.Context, valuer.UUID, authtypes.Relation, authtypes.Typeable, []authtypes.Selector, []authtypes.Selector) error
|
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.
|
// 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, map[string]*openfgav1.TupleKey) (map[string]*authtypes.TupleKeyAuthorization, error)
|
BatchCheck(context.Context, []*openfgav1.TupleKey) error
|
||||||
|
|
||||||
// Write accepts the insertion tuples and the deletion tuples.
|
// Write accepts the insertion tuples and the deletion tuples.
|
||||||
Write(context.Context, []*openfgav1.TupleKey, []*openfgav1.TupleKey) error
|
Write(context.Context, []*openfgav1.TupleKey, []*openfgav1.TupleKey) error
|
||||||
@@ -99,7 +102,5 @@ type Handler interface {
|
|||||||
|
|
||||||
PatchObjects(http.ResponseWriter, *http.Request)
|
PatchObjects(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
Check(http.ResponseWriter, *http.Request)
|
|
||||||
|
|
||||||
Delete(http.ResponseWriter, *http.Request)
|
Delete(http.ResponseWriter, *http.Request)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func (store *store) Create(ctx context.Context, role *roletypes.StorableRole) er
|
|||||||
Model(role).
|
Model(role).
|
||||||
Exec(ctx)
|
Exec(ctx)
|
||||||
if err != nil {
|
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
|
return nil
|
||||||
|
|||||||
@@ -48,7 +48,11 @@ func (provider *provider) Stop(ctx context.Context) error {
|
|||||||
return provider.server.Stop(ctx)
|
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)
|
return provider.server.BatchCheck(ctx, tupleReq)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,6 +181,10 @@ func (provider *provider) CreateManagedRoles(ctx context.Context, _ valuer.UUID,
|
|||||||
return nil
|
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 {
|
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))
|
return provider.Grant(ctx, orgID, roletypes.SigNozAdminRoleName, authtypes.MustNewSubject(authtypes.TypeableUser, userID.String(), orgID, nil))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,18 +90,42 @@ func (server *Server) Stop(ctx context.Context) error {
|
|||||||
return nil
|
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()
|
storeID, modelID := server.getStoreIDandModelID()
|
||||||
batchCheckItems := make([]*openfgav1.BatchCheckItem, 0, len(tupleReq))
|
checkResponse, err := server.openfgaServer.Check(
|
||||||
for id, tuple := range tupleReq {
|
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{
|
batchCheckItems = append(batchCheckItems, &openfgav1.BatchCheckItem{
|
||||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||||
User: tuple.User,
|
User: tuple.User,
|
||||||
Relation: tuple.Relation,
|
Relation: tuple.Relation,
|
||||||
Object: tuple.Object,
|
Object: tuple.Object,
|
||||||
},
|
},
|
||||||
// Use transaction ID as correlation ID for deterministic mapping
|
// the batch check response is map[string] keyed by correlationID.
|
||||||
CorrelationId: id,
|
CorrelationId: strconv.Itoa(idx),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,18 +137,17 @@ func (server *Server) BatchCheck(ctx context.Context, tupleReq map[string]*openf
|
|||||||
Checks: batchCheckItems,
|
Checks: batchCheckItems,
|
||||||
})
|
})
|
||||||
if err != nil {
|
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 _, checkResponse := range checkResponse.Result {
|
||||||
for id, tuple := range tupleReq {
|
if checkResponse.GetAllowed() {
|
||||||
response[id] = &authtypes.TupleKeyAuthorization{
|
return nil
|
||||||
Tuple: tuple,
|
|
||||||
Authorized: checkResponse.Result[id].GetAllowed(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
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
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert slice to map with generated IDs for internal use
|
err = server.BatchCheck(ctx, tuples)
|
||||||
tuples := make(map[string]*openfgav1.TupleKey, len(tupleSlice))
|
|
||||||
for idx, tuple := range tupleSlice {
|
|
||||||
tuples[strconv.Itoa(idx)] = tuple
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := server.BatchCheck(ctx, tuples)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, resp := range response {
|
return nil
|
||||||
if resp.Authorized {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "subjects are not authorized for requested access")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) CheckWithTupleCreationWithoutClaims(ctx context.Context, orgID valuer.UUID, _ authtypes.Relation, _ authtypes.Typeable, _ []authtypes.Selector, roleSelectors []authtypes.Selector) error {
|
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
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert slice to map with generated IDs for internal use
|
err = server.BatchCheck(ctx, tuples)
|
||||||
tuples := make(map[string]*openfgav1.TupleKey, len(tupleSlice))
|
|
||||||
for idx, tuple := range tupleSlice {
|
|
||||||
tuples[strconv.Itoa(idx)] = tuple
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := server.BatchCheck(ctx, tuples)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, resp := range response {
|
return nil
|
||||||
if resp.Authorized {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.Newf(errors.TypeForbidden, authtypes.ErrCodeAuthZForbidden, "subjects are not authorized for requested access")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) Write(ctx context.Context, additions []*openfgav1.TupleKey, deletions []*openfgav1.TupleKey) error {
|
func (server *Server) Write(ctx context.Context, additions []*openfgav1.TupleKey, deletions []*openfgav1.TupleKey) error {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/errors"
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
"github.com/SigNoz/signoz/pkg/http/binding"
|
"github.com/SigNoz/signoz/pkg/http/binding"
|
||||||
"github.com/SigNoz/signoz/pkg/http/render"
|
"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/authtypes"
|
||||||
"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"
|
||||||
@@ -36,14 +35,13 @@ func (handler *handler) Create(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
role := roletypes.NewRole(req.Name, req.Description, roletypes.RoleTypeCustom, valuer.MustNewUUID(claims.OrgID))
|
err = handler.authz.Create(ctx, valuer.MustNewUUID(claims.OrgID), roletypes.NewRole(req.Name, req.Description, roletypes.RoleTypeCustom, valuer.MustNewUUID(claims.OrgID)))
|
||||||
err = handler.authz.Create(ctx, valuer.MustNewUUID(claims.OrgID), role)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
return
|
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) {
|
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) {
|
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) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
render.Success(rw, http.StatusNoContent, nil)
|
render.Success(rw, http.StatusAccepted, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *handler) Delete(rw http.ResponseWriter, r *http.Request) {
|
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)
|
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))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,14 +15,15 @@ var (
|
|||||||
RelationUpdate = Relation{valuer.NewString("update")}
|
RelationUpdate = Relation{valuer.NewString("update")}
|
||||||
RelationDelete = Relation{valuer.NewString("delete")}
|
RelationDelete = Relation{valuer.NewString("delete")}
|
||||||
RelationList = Relation{valuer.NewString("list")}
|
RelationList = Relation{valuer.NewString("list")}
|
||||||
|
RelationBlock = Relation{valuer.NewString("block")}
|
||||||
RelationAssignee = Relation{valuer.NewString("assignee")}
|
RelationAssignee = Relation{valuer.NewString("assignee")}
|
||||||
)
|
)
|
||||||
|
|
||||||
var TypeableRelations = map[Type][]Relation{
|
var TypeableRelations = map[Type][]Relation{
|
||||||
TypeUser: {RelationRead, RelationUpdate, RelationDelete},
|
TypeUser: {RelationRead, RelationUpdate, RelationDelete},
|
||||||
TypeRole: {RelationAssignee, RelationRead, RelationUpdate, RelationDelete},
|
TypeRole: {RelationAssignee, RelationRead, RelationUpdate, RelationDelete},
|
||||||
TypeOrganization: {RelationRead, RelationUpdate, RelationDelete},
|
TypeOrganization: {RelationCreate, RelationRead, RelationUpdate, RelationDelete, RelationList},
|
||||||
TypeMetaResource: {RelationRead, RelationUpdate, RelationDelete},
|
TypeMetaResource: {RelationRead, RelationUpdate, RelationDelete, RelationBlock},
|
||||||
TypeMetaResources: {RelationCreate, RelationList},
|
TypeMetaResources: {RelationCreate, RelationList},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,6 +41,8 @@ func NewRelation(relation string) (Relation, error) {
|
|||||||
return RelationDelete, nil
|
return RelationDelete, nil
|
||||||
case "list":
|
case "list":
|
||||||
return RelationList, nil
|
return RelationList, nil
|
||||||
|
case "block":
|
||||||
|
return RelationBlock, nil
|
||||||
case "assignee":
|
case "assignee":
|
||||||
return RelationAssignee, nil
|
return RelationAssignee, nil
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -6,29 +6,21 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/errors"
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Resource struct {
|
type Resource struct {
|
||||||
Name Name `json:"name" required:"true"`
|
Name Name `json:"name"`
|
||||||
Type Type `json:"type" required:"true"`
|
Type Type `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Object struct {
|
type Object struct {
|
||||||
Resource Resource `json:"resource" required:"true"`
|
Resource Resource `json:"resource"`
|
||||||
Selector Selector `json:"selector" required:"true"`
|
Selector Selector `json:"selector"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
ID valuer.UUID `json:"id"`
|
Relation Relation `json:"relation"`
|
||||||
Relation Relation `json:"relation" required:"true"`
|
Object Object `json:"object"`
|
||||||
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"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewObject(resource Resource, selector Selector) (*Object, error) {
|
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 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
|
return &Transaction{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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (object *Object) UnmarshalJSON(data []byte) error {
|
func (object *Object) UnmarshalJSON(data []byte) error {
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -69,29 +69,24 @@ type StorableRole struct {
|
|||||||
type Role struct {
|
type Role struct {
|
||||||
types.Identifiable
|
types.Identifiable
|
||||||
types.TimeAuditable
|
types.TimeAuditable
|
||||||
Name string `json:"name" required:"true"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description" required:"true"`
|
Description string `json:"description"`
|
||||||
Type valuer.String `json:"type" required:"true"`
|
Type valuer.String `json:"type"`
|
||||||
OrgID valuer.UUID `json:"orgId" required:"true"`
|
OrgID valuer.UUID `json:"orgId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostableRole struct {
|
type PostableRole struct {
|
||||||
Name string `json:"name" required:"true"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PatchableRole struct {
|
type PatchableRole struct {
|
||||||
Description string `json:"description" required:"true"`
|
Description *string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PatchableObjects struct {
|
type PatchableObjects struct {
|
||||||
Additions []*authtypes.Object `json:"additions" required:"true"`
|
Additions []*authtypes.Object `json:"additions"`
|
||||||
Deletions []*authtypes.Object `json:"deletions" required:"true"`
|
Deletions []*authtypes.Object `json:"deletions"`
|
||||||
}
|
|
||||||
|
|
||||||
type GettableResources struct {
|
|
||||||
Resources []*authtypes.Resource `json:"resources" required:"true"`
|
|
||||||
Relations map[authtypes.Type][]authtypes.Relation `json:"relations" required:"true"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStorableRoleFromRole(role *Role) *StorableRole {
|
func NewStorableRoleFromRole(role *Role) *StorableRole {
|
||||||
@@ -142,20 +137,15 @@ func NewManagedRoles(orgID valuer.UUID) []*Role {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGettableResources(resources []*authtypes.Resource) *GettableResources {
|
func (role *Role) PatchMetadata(description *string) error {
|
||||||
return &GettableResources{
|
|
||||||
Resources: resources,
|
|
||||||
Relations: authtypes.TypeableRelations,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (role *Role) PatchMetadata(description string) error {
|
|
||||||
err := role.CanEditDelete()
|
err := role.CanEditDelete()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
role.Description = description
|
if description != nil {
|
||||||
|
role.Description = *description
|
||||||
|
}
|
||||||
role.UpdatedAt = time.Now()
|
role.UpdatedAt = time.Now()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -220,7 +210,7 @@ func (role *PostableRole) UnmarshalJSON(data []byte) error {
|
|||||||
|
|
||||||
func (role *PatchableRole) UnmarshalJSON(data []byte) error {
|
func (role *PatchableRole) UnmarshalJSON(data []byte) error {
|
||||||
type shadowPatchableRole struct {
|
type shadowPatchableRole struct {
|
||||||
Description string `json:"description"`
|
Description *string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var shadowRole shadowPatchableRole
|
var shadowRole shadowPatchableRole
|
||||||
@@ -228,7 +218,7 @@ func (role *PatchableRole) UnmarshalJSON(data []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if shadowRole.Description == "" {
|
if shadowRole.Description == nil {
|
||||||
return errors.New(errors.TypeInvalidInput, ErrCodeRoleEmptyPatch, "empty role patch request received, description must be present")
|
return errors.New(errors.TypeInvalidInput, ErrCodeRoleEmptyPatch, "empty role patch request received, description must be present")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user