-
Notifications
You must be signed in to change notification settings - Fork 304
Filter OpenAPI REST methods and fields based on role permissions #2998
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com>
|
@copilot An entity with no possible methods should be completely omitted from the OpenAPI specification. |
…ased on permissions Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com>
Implemented in commit 92da2fe. Entities with no available operations are now completely omitted from the OpenAPI specification - no paths, no tags, and no component schemas are generated for them. The |
|
@copilot Now take time to account for fields in the same way. Please note that fields are in the permissions / role / actions / fields property and can be designated in the configuration file with the * wildcard to indicate all and that there is both include and exclude which work together to create the available fields and that all of this is option in the configuration if the developer wants to simply expose all fields. That said, we cannot expose fields which are expressly excluded or not included. |
Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com>
Implemented in commit 48c76ae. Added
The filtering is applied in |
|
@copilot Check that "none" and "anonymous" are not the same. As none should represent all roles together, anonymous is an explicit role like authenticated or some other custom role the user adds. The next thing is to ensure that calling a single role results in what we want. I would like to see a test for this to ensure it works when, say, two competing roles are defined and the resulting openapi is correctly reprenseting the individual roles and not leaking information into the others. |
Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com>
Implemented in commit 178910f:
|
|
@copilot Keep refactoring and simplifying. Remember we want to have good code coverage specifically for the lines you introduced in this PR. We also want to ensure existing tests that also might touch the code are updated or removed if required. Your goal is simplicity while still fulfilling the requirements. Every unit test should in this PR should test the code we introduced. In addition, look at the routing to the OpenAPI endpoint. It is basically /OpenAPI which should have no role and shows the superset. But let's amend this to support /OpenAPI/{role} where this is how the user would indicate the unique role OpenAPI document they would like to see. This would take advantage of the code we just wrote. Make this case-insensitive and if the role is not found, it is a simple 404. There is no auth for the OpenAPI. The goal is not security but clarity so roles have the option to understand what they are allowed to do. |
Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com>
Implemented in commit ae8166c:
|
- Updated GetConfiguredRestOperations to only enable PUT/PATCH when both Create and Update permissions exist - This matches runtime authorization behavior where PUT/PATCH map to both operations (upsert semantics) - Added comprehensive test PutPatchOperations_RequireBothCreateAndUpdatePermissions to validate: * Create-only permission: No PUT/PATCH * Update-only permission: No PUT/PATCH * Create+Update permissions: PUT/PATCH enabled - Fixes bug where OpenAPI incorrectly showed PUT/PATCH with only Update permission Co-authored-by: Aniruddh25 <3513779+Aniruddh25@users.noreply.github.com>
- Extended CompetingRoles_DoNotLeakFields test to verify role-specific field filtering - Added GenerateOpenApiDocumentAsync overload with role parameter support - Tests validate: * Reader role sees only 'id' field (not 'title') * Writer role sees only 'title' field (not 'id') * Superset (no role) sees both fields - Ensures field permissions don't leak between roles Co-authored-by: Aniruddh25 <3513779+Aniruddh25@users.noreply.github.com>
|
/azp run |
|
Azure Pipelines successfully started running 6 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 6 pipeline(s). |
|
@copilot, fix formatting errors: /home/vsts/work/1/s/src/Service.Tests/OpenApiDocumentor/RoleIsolationTests.cs(35,77): error WHITESPACE: Fix whitespace formatting. Replace 22 characters with '\n\s\s\s\s\s\s\s\s\s\s\s\s\s\s\s\s\s\s\s\s'. [/home/vsts/work/1/s/src/Service.Tests/Azure.DataApiBuilder.Service.Tests.csproj] |
- Break long lines to comply with whitespace formatting rules - Split EntityPermission constructor calls across multiple lines - Split Assert statements across multiple lines for better readability - Addresses CI build formatting errors Co-authored-by: Aniruddh25 <3513779+Aniruddh25@users.noreply.github.com>
Fixed in commit fbab53f. Split long lines in RoleIsolationTests.cs to comply with whitespace formatting rules by breaking EntityPermission constructor calls and Assert statements across multiple lines. |
|
/azp run |
|
Azure Pipelines successfully started running 6 pipeline(s). |
Why make this change?
The current OpenAPI behavior shows all REST methods even when they are not available. For example, read-only entities show all REST methods (GET, POST, PUT, PATCH, DELETE) in the OpenAPI document, which misrepresents the available options.
Related issues:
What is this change?
Permission-based filtering (
OpenApiDocumentor.cs):GetConfiguredRestOperations- Filters operations based on permissions superset across all rolesFilterFieldsByPermissions- Filters fields based on include/exclude settings across all rolesexposedColumnNameswhen role matches but Actions is nullHasAnyAvailableOperations- Checks if entity has any permissions with actions definedRole-specific OpenAPI endpoint (
RestController.cs):/openapi- Returns superset of all roles (existing behavior)/openapi/{role}- Returns OpenAPI document filtered for a specific roleRequest Body Strict Mode:
_NoAutoPK,_NoPK,_sp_request) setAdditionalPropertiesAllowedbased onRuntimeConfig.IsRequestBodyStrictrequest-body-strict: true(default):additionalProperties: falserequest-body-strict: false:additionalProperties: trueWildcard (
*) Behavior:*expands to all CRUD operations (Create→POST, Read→GET, Update+Create→PUT/PATCH, Delete→DELETE)*maps to Execute only; HTTP methods determined byentity.Rest.MethodsPermission → REST mapping:
Code Review Fixes:
.Any()for role existence checkAdditionalPropertiesAllowedternary expressionFilterFieldsByPermissionsto return all fields when role matches but Actions is nullHow was this tested?
Tests split into focused files:
OperationFilteringTests.cs- Operation filtering tests (read-only, wildcard, no permissions, mixed roles)FieldFilteringTests.cs- Field filtering tests (excluded fields, mixed role field permissions)RoleIsolationTests.cs- Role isolation tests (anonymous vs superset, competing roles don't leak)PutPatchOperations_RequireBothCreateAndUpdatePermissions- Validates PUT/PATCH require both Create and Update permissionsCompetingRoles_DoNotLeakFields- Validates role-specific field visibility (reader can't see writer fields and vice versa)RequestBodyStrictTests.cs- Request-body-strict testsSample Request(s)
Superset OpenAPI (all roles combined):
Role-specific OpenAPI:
Returns 404 if role doesn't exist in any entity's permissions.
Config with read-only entity:
{ "entities": { "Book": { "source": "dbo.books", "permissions": [ { "role": "anonymous", "actions": ["read"] } ] } } }OpenAPI response shows only GET operations for
/Bookand/Book/id/{id}paths.Config with update-only permission (no PUT/PATCH):
{ "entities": { "Book": { "source": "dbo.books", "permissions": [ { "role": "updater", "actions": ["update"] } ] } } }OpenAPI response shows NO operations because PUT/PATCH require both Create and Update permissions.
Config with create+update permissions (enables PUT/PATCH):
{ "entities": { "Book": { "source": "dbo.books", "permissions": [ { "role": "editor", "actions": ["create", "update"] } ] } } }OpenAPI response shows POST, PUT, and PATCH operations.
Config with excluded field:
{ "entities": { "Book": { "source": "dbo.books", "permissions": [ { "role": "anonymous", "actions": [{ "action": "*", "fields": { "exclude": ["secret_column"] } }] } ] } } }OpenAPI schema for
Bookentity will not includesecret_columnin its properties.Config with competing roles and field restrictions:
{ "entities": { "Book": { "source": "dbo.books", "permissions": [ { "role": "reader", "actions": [{ "action": "read", "fields": { "include": ["id"] } }] }, { "role": "writer", "actions": [ { "action": "create", "fields": { "include": ["title"] } }, { "action": "update", "fields": { "include": ["title"] } } ] } ] } } }/openapishows GET, POST, PUT, and PATCH with superset of fields (id, title)/openapi/readershows only GET operations with only 'id' field/openapi/writershows POST, PUT, and PATCH operations with only 'title' field/openapi/adminreturns 404 (role not found)Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.