Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions docs/api-reference/apidocs.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,47 @@
]
}
},
"/v1/tenants/{tenant_id}/permissions/bulk-check": {
"post": {
"summary": "bulk check api",
"description": "Check multiple permissions in a single request. Maximum 100 requests allowed.",
"operationId": "permissions.bulk-check",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/PermissionBulkCheckResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/Status"
}
}
},
"parameters": [
{
"name": "tenant_id",
"description": "Identifier of the tenant, if you are not using multi-tenancy (have only one tenant) use pre-inserted tenant \u003ccode\u003et1\u003c/code\u003e for this field. Required, and must match the pattern \\“[a-zA-Z0-9-,]+\\“, max 64 bytes.",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/BulkCheckBody"
}
}
],
"tags": [
"Permission"
]
}
},
"/v1/tenants/{tenant_id}/permissions/check": {
"post": {
"summary": "check api",
Expand Down Expand Up @@ -1479,6 +1520,36 @@
"default": "ATTRIBUTE_TYPE_UNSPECIFIED",
"description": "Enumerates the types of attribute.\n\n - ATTRIBUTE_TYPE_UNSPECIFIED: Not specified attribute type. This is the default value.\n - ATTRIBUTE_TYPE_BOOLEAN: A boolean attribute type.\n - ATTRIBUTE_TYPE_BOOLEAN_ARRAY: A boolean array attribute type.\n - ATTRIBUTE_TYPE_STRING: A string attribute type.\n - ATTRIBUTE_TYPE_STRING_ARRAY: A string array attribute type.\n - ATTRIBUTE_TYPE_INTEGER: An integer attribute type.\n - ATTRIBUTE_TYPE_INTEGER_ARRAY: An integer array attribute type.\n - ATTRIBUTE_TYPE_DOUBLE: A double attribute type.\n - ATTRIBUTE_TYPE_DOUBLE_ARRAY: A double array attribute type."
},
"BulkCheckBody": {
"type": "object",
"properties": {
"metadata": {
"$ref": "#/definitions/PermissionCheckRequestMetadata",
"description": "Metadata associated with this request, required."
},
"items": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/PermissionBulkCheckRequestItem"
},
"description": "List of permission check requests, maximum 100 items."
},
"context": {
"$ref": "#/definitions/Context",
"description": "Contextual data that can be dynamically added to permission check requests. See details on [Contextual Data](../../operations/contextual-tuples)"
},
"arguments": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/Argument"
},
"description": "Additional arguments associated with this request."
}
},
"description": "PermissionBulkCheckRequest is the request message for the BulkCheck method in the Permission service."
},
"Bundle.DeleteBody": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -2487,6 +2558,39 @@
},
"description": "PermissionExpandRequest is the request message for the Expand method in the Permission service."
},
"PermissionBulkCheckRequestItem": {
"type": "object",
"properties": {
"entity": {
"$ref": "#/definitions/Entity",
"example": "repository:1",
"description": "Entity on which the permission needs to be checked, required."
},
"permission": {
"type": "string",
"description": "The action the user wants to perform on the resource"
},
"subject": {
"$ref": "#/definitions/Subject",
"description": "Subject for which the permission needs to be checked, required."
}
},
"title": "BULK CHECK"
},
"PermissionBulkCheckResponse": {
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/PermissionCheckResponse"
},
"description": "List of permission check responses corresponding to each request."
}
},
"description": "PermissionBulkCheckResponse is the response message for the BulkCheck method in the Permission service."
},
"PermissionCheckRequestMetadata": {
"type": "object",
"properties": {
Expand Down
104 changes: 104 additions & 0 deletions docs/api-reference/openapiv2/apidocs.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,47 @@
]
}
},
"/v1/tenants/{tenant_id}/permissions/bulk-check": {
"post": {
"summary": "bulk check api",
"description": "Check multiple permissions in a single request. Maximum 100 requests allowed.",
"operationId": "permissions.bulk-check",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/PermissionBulkCheckResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/Status"
}
}
},
"parameters": [
{
"name": "tenant_id",
"description": "Identifier of the tenant, if you are not using multi-tenancy (have only one tenant) use pre-inserted tenant \u003ccode\u003et1\u003c/code\u003e for this field. Required, and must match the pattern \\“[a-zA-Z0-9-,]+\\“, max 64 bytes.",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/BulkCheckBody"
}
}
],
"tags": [
"Permission"
]
}
},
"/v1/tenants/{tenant_id}/permissions/check": {
"post": {
"summary": "check api",
Expand Down Expand Up @@ -1477,6 +1518,36 @@
],
"description": "Enumerates the types of attribute.\n\n - ATTRIBUTE_TYPE_BOOLEAN: A boolean attribute type.\n - ATTRIBUTE_TYPE_BOOLEAN_ARRAY: A boolean array attribute type.\n - ATTRIBUTE_TYPE_STRING: A string attribute type.\n - ATTRIBUTE_TYPE_STRING_ARRAY: A string array attribute type.\n - ATTRIBUTE_TYPE_INTEGER: An integer attribute type.\n - ATTRIBUTE_TYPE_INTEGER_ARRAY: An integer array attribute type.\n - ATTRIBUTE_TYPE_DOUBLE: A double attribute type.\n - ATTRIBUTE_TYPE_DOUBLE_ARRAY: A double array attribute type."
},
"BulkCheckBody": {
"type": "object",
"properties": {
"metadata": {
"$ref": "#/definitions/PermissionCheckRequestMetadata",
"description": "Metadata associated with this request, required."
},
"items": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/PermissionBulkCheckRequestItem"
},
"description": "List of permission check requests, maximum 100 items."
},
"context": {
"$ref": "#/definitions/Context",
"description": "Contextual data that can be dynamically added to permission check requests. See details on [Contextual Data](../../operations/contextual-tuples)"
},
"arguments": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/Argument"
},
"description": "Additional arguments associated with this request."
}
},
"description": "PermissionBulkCheckRequest is the request message for the BulkCheck method in the Permission service."
},
"Bundle.DeleteBody": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -2471,6 +2542,39 @@
},
"description": "PermissionExpandRequest is the request message for the Expand method in the Permission service."
},
"PermissionBulkCheckRequestItem": {
"type": "object",
"properties": {
"entity": {
"$ref": "#/definitions/Entity",
"example": "repository:1",
"description": "Entity on which the permission needs to be checked, required."
},
"permission": {
"type": "string",
"description": "The action the user wants to perform on the resource"
},
"subject": {
"$ref": "#/definitions/Subject",
"description": "Subject for which the permission needs to be checked, required."
}
},
"title": "BULK CHECK"
},
"PermissionBulkCheckResponse": {
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/PermissionCheckResponse"
},
"description": "List of permission check responses corresponding to each request."
}
},
"description": "PermissionBulkCheckResponse is the response message for the BulkCheck method in the Permission service."
},
"PermissionCheckRequestMetadata": {
"type": "object",
"properties": {
Expand Down
76 changes: 76 additions & 0 deletions internal/servers/permission_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,82 @@ func (r *PermissionServer) Check(ctx context.Context, request *v1.PermissionChec
return response, nil
}

// BulkCheck - Performs multiple authorization checks in a single request
func (r *PermissionServer) BulkCheck(ctx context.Context, request *v1.PermissionBulkCheckRequest) (*v1.PermissionBulkCheckResponse, error) {
ctx, span := internal.Tracer.Start(ctx, "permissions.bulk-check")
defer span.End()

// Validate tenant_id
if request.GetTenantId() == "" {
err := status.Error(GetStatus(nil), "tenant_id is required")
span.RecordError(err)
span.SetStatus(otelCodes.Error, err.Error())
return nil, err
}

// Validate number of requests
if len(request.GetItems()) == 0 {
err := status.Error(GetStatus(nil), "at least one item is required")
span.RecordError(err)
span.SetStatus(otelCodes.Error, err.Error())
return nil, err
}

if len(request.GetItems()) > 100 {
err := status.Error(GetStatus(nil), "maximum 100 items allowed")
span.RecordError(err)
span.SetStatus(otelCodes.Error, err.Error())
return nil, err
}

// Process each check request
results := make([]*v1.PermissionCheckResponse, len(request.GetItems()))
for i, checkRequestItem := range request.GetItems() {

// Validate individual request
v := checkRequestItem.Validate()
if v != nil {
// Return error response for this check
results[i] = &v1.PermissionCheckResponse{
Can: v1.CheckResult_CHECK_RESULT_DENIED,
Metadata: &v1.PermissionCheckResponseMetadata{
CheckCount: 0,
},
}
continue
}
Comment on lines +82 to +93
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Consider providing validation error details to callers.

When an item fails validation, the response only includes CHECK_RESULT_DENIED without any indication of what validation rule failed. This makes it difficult for API consumers to debug issues. Consider one of these approaches:

  1. Add an optional error message field to PermissionCheckResponse to include validation errors
  2. Log the validation error with the item index for easier server-side debugging
  3. Document that validation errors result in DENIED responses

If adding error details isn't feasible, at least add structured logging:

 		// Validate individual request
 		v := checkRequestItem.Validate()
 		if v != nil {
+			slog.ErrorContext(ctx, "item validation failed in bulk operation", "error", v.Error(), "index", i)
 			// Return error response for this check
 			results[i] = &v1.PermissionCheckResponse{
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Validate individual request
v := checkRequestItem.Validate()
if v != nil {
// Return error response for this check
results[i] = &v1.PermissionCheckResponse{
Can: v1.CheckResult_CHECK_RESULT_DENIED,
Metadata: &v1.PermissionCheckResponseMetadata{
CheckCount: 0,
},
}
continue
}
// Validate individual request
v := checkRequestItem.Validate()
if v != nil {
slog.ErrorContext(ctx, "item validation failed in bulk operation", "error", v.Error(), "index", i)
// Return error response for this check
results[i] = &v1.PermissionCheckResponse{
Can: v1.CheckResult_CHECK_RESULT_DENIED,
Metadata: &v1.PermissionCheckResponseMetadata{
CheckCount: 0,
},
}
continue
}
🤖 Prompt for AI Agents
In internal/servers/permission_server.go around lines 82–93, when a request item
fails validation the code currently returns a DENIED result with no error
details; update handling to (1) add a validation error message into the response
(either by adding an optional ErrorMessage string on PermissionCheckResponse or
adding an Error field on PermissionCheckResponseMetadata) and populate it with
v.Error() (or v.Error() equivalent) for the failing item, and (2) add a
structured server log entry including the item index and the validation error
before continuing so server-side debugging is possible; ensure the proto change
(if adding a field) is backward-compatible/optional and update any
marshaling/use sites accordingly.


// Perform the check using existing Check function
checkRequest := &v1.PermissionCheckRequest{
TenantId: request.GetTenantId(),
Subject: checkRequestItem.GetSubject(),
Entity: checkRequestItem.GetEntity(),
Permission: checkRequestItem.GetPermission(),
Metadata: request.GetMetadata(),
Context: request.GetContext(),
Arguments: request.GetArguments(),
}
Comment on lines +96 to +104
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix formatting inconsistency.

Line 101 uses tabs for indentation while the surrounding lines use spaces. This creates inconsistent formatting.

Apply this diff to fix the indentation:

 		checkRequest := &v1.PermissionCheckRequest{
 			TenantId:      request.GetTenantId(),
 			Subject:       checkRequestItem.GetSubject(),
 			Entity:        checkRequestItem.GetEntity(),
 			Permission:    checkRequestItem.GetPermission(),
-			Metadata: 	   request.GetMetadata(),
+			Metadata:      request.GetMetadata(),
 			Context:       request.GetContext(),
 			Arguments:     request.GetArguments(),
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
checkRequest := &v1.PermissionCheckRequest{
TenantId: request.GetTenantId(),
Subject: checkRequestItem.GetSubject(),
Entity: checkRequestItem.GetEntity(),
Permission: checkRequestItem.GetPermission(),
Metadata: request.GetMetadata(),
Context: request.GetContext(),
Arguments: request.GetArguments(),
}
checkRequest := &v1.PermissionCheckRequest{
TenantId: request.GetTenantId(),
Subject: checkRequestItem.GetSubject(),
Entity: checkRequestItem.GetEntity(),
Permission: checkRequestItem.GetPermission(),
Metadata: request.GetMetadata(),
Context: request.GetContext(),
Arguments: request.GetArguments(),
}
🤖 Prompt for AI Agents
In internal/servers/permission_server.go around lines 96 to 104, the indentation
on line 101 uses a tab for the Metadata field unlike the surrounding lines which
use spaces; replace the tab with the same number of spaces used by the other
struct fields so the alignment matches (use the project's existing spacing style
for struct literal fields) and ensure the file passes gofmt/golint formatting
checks.

response, err := r.invoker.Check(ctx, checkRequest)
if err != nil {
// Log error but don't fail the entire bulk operation
slog.ErrorContext(ctx, "check failed in bulk operation", "error", err.Error(), "index", i)
results[i] = &v1.PermissionCheckResponse{
Can: v1.CheckResult_CHECK_RESULT_DENIED,
Metadata: &v1.PermissionCheckResponseMetadata{
CheckCount: 0,
},
}
continue
}

results[i] = response
}

return &v1.PermissionBulkCheckResponse{
Results: results,
}, nil
}

// Expand - Get schema actions in a tree structure
func (r *PermissionServer) Expand(ctx context.Context, request *v1.PermissionExpandRequest) (*v1.PermissionExpandResponse, error) {
ctx, span := internal.Tracer.Start(ctx, "permissions.expand")
Expand Down
Loading