🐛 fix: cannot get project list by access tokens#878
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes project retrieval via access tokens by centralizing request permission resolution and using those permissions to filter returned projects/environments (and to deny access when appropriate).
Changes:
- Switched permissions shapes from
IEnumerable<PolicyStatement>toPolicyStatement[]across access tokens and member permission retrieval. - Added scoped
IRequestPermissionsservice to resolve/cached request permissions for both JWT users and OpenAPI access tokens, and wired it into authorization + controllers. - Updated project “get” and “list” flows to filter environments based on the resolved permissions and return 403 via a new
ForbiddenExceptionpath.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| modules/back-end/src/Infrastructure/Services/MongoDb/MemberService.cs | Return permissions as PolicyStatement[] (materialized). |
| modules/back-end/src/Infrastructure/Services/EntityFrameworkCore/MemberService.cs | Same as above for EF Core implementation. |
| modules/back-end/src/Domain/AccessTokens/AccessToken.cs | Store token permissions as PolicyStatement[] (better for persistence/serialization). |
| modules/back-end/src/Application/Services/IMemberService.cs | Updated signature to return PolicyStatement[]. |
| modules/back-end/src/Application/Projects/GetProjectList.cs | Accept request permissions and filter projects/envs based on them. |
| modules/back-end/src/Application/Projects/GetProject.cs | Enforce project access and filter returned environments based on permissions. |
| modules/back-end/src/Application/Bases/Exceptions/ForbiddenException.cs | New exception used to signal 403 from application layer. |
| modules/back-end/src/Application/AccessTokens/CreateAccessToken.cs | Updated permissions type to array and adjusted validation predicate. |
| modules/back-end/src/Api/Setup/ServicesRegister.cs | Registers IRequestPermissions as a scoped service. |
| modules/back-end/src/Api/Middlewares/ApiExceptionMiddlewareExtension.cs | Maps ForbiddenException to HTTP 403 + ErrorCodes.Forbidden. |
| modules/back-end/src/Api/Controllers/ProjectController.cs | Fetches request permissions and passes them into MediatR requests. |
| modules/back-end/src/Api/Controllers/ApiControllerBase.cs | Adds helper to retrieve PolicyStatement[] via IRequestPermissions. |
| modules/back-end/src/Api/Authorization/RequestPermissions.cs | Implements permission resolution for JWT vs OpenAPI access tokens (with request-scope caching). |
| modules/back-end/src/Api/Authorization/IRequestPermissions.cs | New interface for request permission resolution. |
| modules/back-end/src/Api/Authorization/DefaultPermissionChecker.cs | Now consumes IRequestPermissions instead of duplicating resolution logic. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
This PR fixes authorization behavior for project listing when authenticated via OpenApi access tokens, and adds env-level filtering based on the current request’s permissions.
Changes:
- Introduce a scoped
IRequestPermissionsservice to resolve/cachePolicyStatement[]for the current request (JWT user vs OpenApi access token). - Pass resolved permissions into
GetProjectList/GetProjecthandlers and filter out inaccessible projects/environments. - Add a
ForbiddenExceptionand map it to a 403 response in the API exception middleware.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| modules/back-end/src/Infrastructure/Services/MongoDb/MemberService.cs | Change permissions return type to PolicyStatement[] and materialize statements. |
| modules/back-end/src/Infrastructure/Services/EntityFrameworkCore/MemberService.cs | Change permissions return type to PolicyStatement[] and materialize statements. |
| modules/back-end/src/Domain/AccessTokens/AccessToken.cs | Change Permissions to PolicyStatement[] on the domain entity. |
| modules/back-end/src/Application/Services/IMemberService.cs | Update GetPermissionsAsync signature to return PolicyStatement[]. |
| modules/back-end/src/Application/Projects/GetProjectList.cs | Remove ICurrentUser dependency; filter projects/envs using request-provided permissions. |
| modules/back-end/src/Application/Projects/GetProject.cs | Enforce project permission + filter environments using request-provided permissions. |
| modules/back-end/src/Application/Bases/Exceptions/ForbiddenException.cs | Add a dedicated exception type for 403 flows. |
| modules/back-end/src/Application/AccessTokens/CreateAccessToken.cs | Change request Permissions to PolicyStatement[] and adjust validation. |
| modules/back-end/src/Api/Setup/ServicesRegister.cs | Register IRequestPermissions as scoped. |
| modules/back-end/src/Api/Middlewares/ApiExceptionMiddlewareExtension.cs | Map ForbiddenException to a 403 response with ErrorCodes.Forbidden. |
| modules/back-end/src/Api/Controllers/ProjectController.cs | Resolve request permissions and pass into project queries; remove action-level permission attribute on Get-by-id. |
| modules/back-end/src/Api/Controllers/ApiControllerBase.cs | Add helper to resolve request permissions via DI. |
| modules/back-end/src/Api/Authorization/RequestPermissions.cs | New service resolving permissions based on auth scheme and caching per request. |
| modules/back-end/src/Api/Authorization/IRequestPermissions.cs | New interface for request-scoped permission resolution. |
| modules/back-end/src/Api/Authorization/DefaultPermissionChecker.cs | Refactor to use IRequestPermissions instead of duplicating resolution logic. |
Comments suppressed due to low confidence (1)
modules/back-end/src/Domain/AccessTokens/AccessToken.cs:36
AccessToken.Permissionsis now a non-nullable array, but it can still benullat runtime (e.g., existing rows/documents with a missing/NULLPermissionsfield, or personal tokens created without permissions). This can later cause 500s when code assumes an enumerable (e.g., permission checks). Consider default-initializing the property (e.g., empty array) and/or normalizingpermissionsto an empty array in the constructor to enforce a non-null invariant.
public PolicyStatement[] Permissions { get; set; }
public DateTime? LastUsedAt { get; set; }
public AccessToken(
Guid organizationId,
Guid creatorId,
string name,
string type,
PolicyStatement[] permissions)
{
OrganizationId = organizationId;
CreatorId = creatorId;
Name = name;
Status = AccessTokenStatus.Active;
Type = type;
Permissions = permissions;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Fixes project retrieval/listing when authenticated via OpenAPI access tokens by resolving permissions from the current request context (instead of ICurrentUser), filtering inaccessible environments, and ensuring projects still appear even when no environments are accessible.
Changes:
- Standardize permissions payloads to
PolicyStatement[](member permissions + access token stored permissions) to avoid serialization/materialization issues. - Introduce
IRequestPermissions/RequestPermissions(scoped) and reuse it inDefaultPermissionCheckerand controllers to obtain per-request permissions for both JWT and OpenAPI schemes. - Filter environments by permission in
GetProject/GetProjectList, and return projects even when the accessible env list is empty; add a dedicatedForbiddenExceptionmapped to HTTP 403.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| modules/back-end/src/Infrastructure/Services/MongoDb/MemberService.cs | Return permissions as PolicyStatement[] to provide a concrete, materialized collection. |
| modules/back-end/src/Infrastructure/Services/EntityFrameworkCore/MemberService.cs | Same as Mongo: return a materialized PolicyStatement[]. |
| modules/back-end/src/Domain/AccessTokens/AccessToken.cs | Change stored token permissions to PolicyStatement[] to support persistence/materialization. |
| modules/back-end/src/Application/Services/IMemberService.cs | Update contract to return PolicyStatement[] for permissions. |
| modules/back-end/src/Application/Projects/GetProjectList.cs | Use request-provided permissions to filter projects/envs; keep projects even with zero accessible envs. |
| modules/back-end/src/Application/Projects/GetProject.cs | Enforce project access + filter envs based on request permissions; throw ForbiddenException when disallowed. |
| modules/back-end/src/Application/Bases/Exceptions/ForbiddenException.cs | Add a dedicated exception to represent forbidden access at the application layer. |
| modules/back-end/src/Application/AccessTokens/CreateAccessToken.cs | Store permissions as an array and validate permissions presence/emptiness for non-personal tokens. |
| modules/back-end/src/Api/Setup/ServicesRegister.cs | Register IRequestPermissions as scoped for per-request permission resolution. |
| modules/back-end/src/Api/Middlewares/ApiExceptionMiddlewareExtension.cs | Map ForbiddenException to HTTP 403 with ErrorCodes.Forbidden. |
| modules/back-end/src/Api/Controllers/ProjectController.cs | Pass per-request permissions into project queries to support access-token auth and env filtering. |
| modules/back-end/src/Api/Controllers/ApiControllerBase.cs | Add GetRequestPermissionsAsync() helper to fetch request permissions via DI. |
| modules/back-end/src/Api/Authorization/RequestPermissions.cs | New scoped service computing permissions for JWT vs OpenAPI access token requests. |
| modules/back-end/src/Api/Authorization/IRequestPermissions.cs | New interface for request-scoped permission resolution. |
| modules/back-end/src/Api/Authorization/DefaultPermissionChecker.cs | Consume IRequestPermissions instead of duplicating permission-resolution logic. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Uh oh!
There was an error while loading. Please reload this page.