Skip to content

Backend agent table public permissions - #1864

Merged
Artuomka merged 2 commits into
mainfrom
backend_agent_table_public_permissions
Aug 4, 2026
Merged

Backend agent table public permissions#1864
Artuomka merged 2 commits into
mainfrom
backend_agent_table_public_permissions

Conversation

@Artuomka

@Artuomka Artuomka commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features
    • Added internal endpoints to retrieve connection details and validate public table-read access.
    • Added support for returning end-user token signing information with connection data.
    • Added validation for publicly readable tables and columns, including filtering to permitted columns.
    • Added request and response validation for connection retrieval and public-read checks.

Copilot AI review requested due to automatic review settings August 4, 2026 11:37
@Artuomka
Artuomka enabled auto-merge August 4, 2026 11:38
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds internal Sitenova endpoints for retrieving decrypted runtime connections and validating public table reads. The change introduces typed contracts, use cases, provider registrations, end-user signing-key access, and Cedar-based column filtering.

Changes

Sitenova internal access

Layer / File(s) Summary
Contracts and provider wiring
backend/src/common/data-injection.tokens.ts, backend/src/microservices/sitenova-microservice/data-structures/*, backend/src/microservices/sitenova-microservice/dto/sitenova.dtos.ts, backend/src/microservices/sitenova-microservice/use-cases/sitenova-use-cases.interface.ts, backend/src/microservices/sitenova-microservice/sitenova.module.ts
Adds typed inputs, responses, use-case interfaces, validation metadata, injection tokens, and module providers.
Connection retrieval flow
backend/src/microservices/sitenova-microservice/services/sitenova-enduser-auth.service.ts, backend/src/microservices/sitenova-microservice/use-cases/sitenova-get-connection.use.case.ts, backend/src/microservices/sitenova-microservice/sitenova-internal.controller.ts
Retrieves and decrypts valid connections, rejects agent connections without stored credentials, includes the end-user signing key, and exposes the GET endpoint.
Public-read validation flow
backend/src/microservices/sitenova-microservice/use-cases/sitenova-validate-public-read.use.case.ts, backend/src/microservices/sitenova-microservice/sitenova-internal.controller.ts
Checks public access and table authorization, then returns only Cedar-readable requested columns through the POST endpoint.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant InternalClient
  participant SitenovaInternalController
  participant SitenovaGetConnectionUseCase
  participant SitenovaEndUserAuthService
  InternalClient->>SitenovaInternalController: GET connection by connectionId
  SitenovaInternalController->>SitenovaGetConnectionUseCase: execute connectionId
  SitenovaGetConnectionUseCase->>SitenovaEndUserAuthService: getEndUserSigningKey(connectionId)
  SitenovaEndUserAuthService-->>SitenovaGetConnectionUseCase: signing key
  SitenovaGetConnectionUseCase-->>SitenovaInternalController: runtime connection data
  SitenovaInternalController-->>InternalClient: connection response
Loading
sequenceDiagram
  participant InternalClient
  participant SitenovaInternalController
  participant SitenovaValidatePublicReadUseCase
  participant CedarPermissionsService
  InternalClient->>SitenovaInternalController: POST table and column validation
  SitenovaInternalController->>SitenovaValidatePublicReadUseCase: execute validation request
  SitenovaValidatePublicReadUseCase->>CedarPermissionsService: check public access and readable columns
  CedarPermissionsService-->>SitenovaValidatePublicReadUseCase: authorization result
  SitenovaValidatePublicReadUseCase-->>SitenovaInternalController: validation response
  SitenovaInternalController-->>InternalClient: allowed and readable columns
Loading

Possibly related PRs

Suggested reviewers: copilot, lyubov-voloshko

Poem

A rabbit checks the table bright,
Cedar guards each column’s right.
Keys are fetched and secrets flow,
Valid reads pass; denied reads go.
Sitenova hops through the gate.

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Security Check ⚠️ Warning PR adds /internal/sitenova/connection endpoint without authorization check for connection ownership. Any authenticated microservice can retrieve any connection's decrypted credentials+JWT key, enab... Add authorization check in SitenovaGetConnectionUseCase to validate connection ownership before returning decrypted credentials and JWT key to calling microservice.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately identifies the backend changes for agent table public permissions, which is the primary purpose of the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch backend_agent_table_public_permissions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai
coderabbitai Bot requested a review from lyubov-voloshko August 4, 2026 11:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the backend SiteNova microservice’s internal (microservice-JWT) API to support satellite/universal-backend runtimes by (1) handing out decrypted connection credentials + end-user JWT signing key, and (2) validating anonymous/public table-read permissions (including readable-column projection) using the same Cedar policy logic as the core.

Changes:

  • Added internal endpoints to fetch decrypted connection info (+ end-user JWT signing key) and to validate public-read access + readable columns.
  • Introduced new SiteNova internal request/response DTOs/DS/ROs and new use cases, wired through DI tokens and the module.
  • Exposed an auth-service method to retrieve the per-connection end-user JWT signing key for internal callers.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
backend/src/microservices/sitenova-microservice/use-cases/sitenova-validate-public-read.use.case.ts New use case bridging Cedar public-read evaluation and readable-columns projection for satellites.
backend/src/microservices/sitenova-microservice/use-cases/sitenova-use-cases.interface.ts Added interfaces and typings for new internal use cases.
backend/src/microservices/sitenova-microservice/use-cases/sitenova-get-connection.use.case.ts New use case to return decrypted connection credentials and end-user JWT signing key (rejects agent connections).
backend/src/microservices/sitenova-microservice/sitenova.module.ts Registered new use cases in DI container for the SiteNova microservice.
backend/src/microservices/sitenova-microservice/sitenova-internal.controller.ts Added internal routes for connection credential retrieval and public-read validation.
backend/src/microservices/sitenova-microservice/services/sitenova-enduser-auth.service.ts Added a public method to expose the end-user signing key to internal callers.
backend/src/microservices/sitenova-microservice/dto/sitenova.dtos.ts Added request DTO for public-read validation (tableName + optional columnNames).
backend/src/microservices/sitenova-microservice/data-structures/sitenova.ds.ts Added DS classes for the new internal use cases.
backend/src/microservices/sitenova-microservice/data-structures/sitenova-internal-responses.ds.ts New internal response objects for decrypted connection payload + public-read validation result.
backend/src/common/data-injection.tokens.ts Added UseCaseType tokens for the new SiteNova internal use cases.
Suppressed comments (1)

backend/src/microservices/sitenova-microservice/use-cases/sitenova-validate-public-read.use.case.ts:50

  • When columnNames is provided as an empty array, readableColumns stays null. This makes the response indistinguishable from the "columnNames was not submitted" case, and contradicts the response description (null only when not submitted). Returning an empty array when an empty array is submitted keeps the API semantics consistent.
		let readableColumns: Array<string> | null = null;
		if (columnNames && columnNames.length > 0) {
			const readable = await this.cedarPermissions.getReadableColumnsForPublic(connectionId, tableName, columnNames);
			readableColumns = columnNames.filter((columnName) => readable.has(columnName));
		}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1 to +26
import { Injectable } from '@nestjs/common';
import AbstractUseCase from '../../../common/abstract-use.case.js';
import { IDatabaseContext } from '../../../common/database-context.interface.js';
import { CedarAction, PUBLIC_USER_ID } from '../../../entities/cedar-authorization/cedar-action-map.js';
import { CedarAuthorizationService } from '../../../entities/cedar-authorization/cedar-authorization.service.js';
import { CedarPermissionsService } from '../../../entities/cedar-authorization/cedar-permissions.service.js';
import { SitenovaValidatePublicReadDs } from '../data-structures/sitenova.ds.js';
import { SitenovaPublicReadValidationRO } from '../data-structures/sitenova-internal-responses.ds.js';
import { ISitenovaValidatePublicRead } from './sitenova-use-cases.interface.js';

// Bridges SitenovaPublicReadGuard's Cedar evaluation (allowed?) and the public readable-columns
// projection to the universal-backend service, which runs the actual query itself but must apply
// the exact same public-read policy the core applies on its own /sitenova data routes.
@Injectable()
export class SitenovaValidatePublicReadUseCase
extends AbstractUseCase<SitenovaValidatePublicReadDs, SitenovaPublicReadValidationRO>
implements ISitenovaValidatePublicRead
{
protected _dbContext: IDatabaseContext | null = null;

constructor(
private readonly cedarAuthService: CedarAuthorizationService,
private readonly cedarPermissions: CedarPermissionsService,
) {
super();
}
Comment on lines +42 to +78
@ApiOperation({
summary: 'Return decrypted connection credentials plus the end-user JWT signing key.',
description:
'Serves the universal-backend service so it can open the customer database itself and ' +
'sign/verify generated-site end-user tokens. Agent connections are rejected (no stored credentials).',
})
@ApiResponse({ status: 200, type: SitenovaConnectionForSiteRuntimeRO })
@Get('/connection/:connectionId')
public async getConnection(
@SlugUuid('connectionId') connectionId: string,
): Promise<SitenovaConnectionForSiteRuntimeRO> {
return await this.getConnectionUseCase.execute({ connectionId }, InTransactionEnum.OFF);
}

@ApiOperation({
summary: 'Evaluate the connection public policy for an anonymous table read.',
description:
'Same Cedar evaluation SitenovaPublicReadGuard applies on the core /sitenova data routes, plus ' +
'the publicly readable subset of the submitted columns — for satellites running the query themselves.',
})
@ApiResponse({ status: 200, type: SitenovaPublicReadValidationRO })
@ApiBody({ type: SitenovaValidatePublicReadDto })
@HttpCode(HttpStatus.OK)
@Post('/validate-public-read/:connectionId')
public async validatePublicRead(
@SlugUuid('connectionId') connectionId: string,
@Body() body: SitenovaValidatePublicReadDto,
): Promise<SitenovaPublicReadValidationRO> {
return await this.validatePublicReadUseCase.execute(
{
connectionId,
tableName: body.tableName,
columnNames: body.columnNames ?? null,
},
InTransactionEnum.OFF,
);
}
@Artuomka
Artuomka merged commit 24d01cb into main Aug 4, 2026
16 of 18 checks passed
@Artuomka
Artuomka deleted the backend_agent_table_public_permissions branch August 4, 2026 11:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
backend/src/microservices/sitenova-microservice/data-structures/sitenova-internal-responses.ds.ts (1)

59-61: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace string concatenation with template literals.

Both Swagger descriptions violate the same repository rule.

  • backend/src/microservices/sitenova-microservice/data-structures/sitenova-internal-responses.ds.ts#L59-L61: use one template literal for the signing-key description.
  • backend/src/microservices/sitenova-microservice/sitenova-internal.controller.ts#L44-L46: use one template literal for the endpoint description.

As per coding guidelines, “Use template literals instead of string concatenation.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/src/microservices/sitenova-microservice/data-structures/sitenova-internal-responses.ds.ts`
around lines 59 - 61, Replace the concatenated Swagger description in
backend/src/microservices/sitenova-microservice/data-structures/sitenova-internal-responses.ds.ts
lines 59-61 with a single template literal. Apply the same conversion to the
endpoint description in
backend/src/microservices/sitenova-microservice/sitenova-internal.controller.ts
lines 44-46; preserve the existing text exactly.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@backend/src/microservices/sitenova-microservice/sitenova-internal.controller.ts`:
- Around line 49-53: The getConnection endpoint currently trusts the
caller-controlled connectionId and returns decrypted credentials without
connection-level authorization. Update getConnection and its authorization flow
to require a valid connections[*] or service authorization claim, or an
equivalent guard proving access to that specific connection, before invoking
getConnectionUseCase.execute and returning secrets; reject unauthorized or
mismatched connection requests.

---

Nitpick comments:
In
`@backend/src/microservices/sitenova-microservice/data-structures/sitenova-internal-responses.ds.ts`:
- Around line 59-61: Replace the concatenated Swagger description in
backend/src/microservices/sitenova-microservice/data-structures/sitenova-internal-responses.ds.ts
lines 59-61 with a single template literal. Apply the same conversion to the
endpoint description in
backend/src/microservices/sitenova-microservice/sitenova-internal.controller.ts
lines 44-46; preserve the existing text exactly.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f00fae52-d5dd-484b-9a03-9d9c065319e8

📥 Commits

Reviewing files that changed from the base of the PR and between fc56fd0 and 3eb5dd4.

📒 Files selected for processing (10)
  • backend/src/common/data-injection.tokens.ts
  • backend/src/microservices/sitenova-microservice/data-structures/sitenova-internal-responses.ds.ts
  • backend/src/microservices/sitenova-microservice/data-structures/sitenova.ds.ts
  • backend/src/microservices/sitenova-microservice/dto/sitenova.dtos.ts
  • backend/src/microservices/sitenova-microservice/services/sitenova-enduser-auth.service.ts
  • backend/src/microservices/sitenova-microservice/sitenova-internal.controller.ts
  • backend/src/microservices/sitenova-microservice/sitenova.module.ts
  • backend/src/microservices/sitenova-microservice/use-cases/sitenova-get-connection.use.case.ts
  • backend/src/microservices/sitenova-microservice/use-cases/sitenova-use-cases.interface.ts
  • backend/src/microservices/sitenova-microservice/use-cases/sitenova-validate-public-read.use.case.ts

Comment on lines +49 to +53
@Get('/connection/:connectionId')
public async getConnection(
@SlugUuid('connectionId') connectionId: string,
): Promise<SitenovaConnectionForSiteRuntimeRO> {
return await this.getConnectionUseCase.execute({ connectionId }, InTransactionEnum.OFF);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

controller="$(fd -a 'sitenova-internal.controller.ts' | head -n1)"
sed -n '1,120p' "$controller"

rg -n -C5 \
  'UseGuards|UseInterceptors|Jwt|JWT|microservice|SitenovaInternalController|connectionId' \
  backend/src

Repository: rocket-admin/rocketadmin

Length of output: 50381


🏁 Script executed:

#!/bin/bash
set -euo pipefail

controller="$(fd -a 'sitenova-internal.controller.ts' | head -n1)"
module="$(fd -a 'sitenova.module.ts' | head -n1)"

echo "== controller =="
sed -n '1,140p' "$controller"

echo
echo "== sitenova module =="
sed -n '1,180p' "$module"

echo
echo "== auth guard decorators/modules in sitenova internal area =="
rg -n --iglob '*.ts' \
  'UseGuards|Guard|HttpUnauthorized|UnauthorizedException|Jwt|jwt|SitenovaInternal|`@Controller`' \
  backend/src/microservices/sitenova-microservice backend/src/common backend/src/decorators backend/src/guards backend/src/filters 2>/dev/null \
  | head -n 200

echo
echo "== focused UseGuards/Jwt/JWT in relevant files =="
while IFS= read -r f; do
  echo "--- $f"
  rg -n -C3 '(UseGuards|AuthGuard|jwt|jwt|JWT|microservice|sitenova-internal|ConnectionReadGuard|Sitenova' "$f" \
    | head -n 120
done < <(fd -a --iglob '*.ts' backend/src | rg 'sitenova|auth-guard|connection|microservice|jwt|interceptor|guard' | head -n 80)

Repository: rocket-admin/rocketadmin

Length of output: 18051


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== SaaSAuthMiddleware =="
fd -a 'saas-auth.middleware.ts' backend/src | sed -n '1,20p' | while read -r f; do
  echo "--- $f"
  sed -n '1,240p' "$f"
done

echo
echo "== SitenovaGetConnectionUseCase =="
usecase="$(fd -a 'sitenova-get-connection.use.case.ts' backend/src | head -n1)"
sed -n '1,160p' "$usecase"

echo
echo "== connection decrypt/signing key methods =="
rg -n -C4 'getDecrypted|decrypt.*password|endUserJwtKey|getEndUserSigningKey|secret-access-log|secret.*access|SitenovaGetConnection' backend/src/microservices/sitenova-microservice backend/src/common backend/src/authorization backend/src/entities -g '*.ts' | head -n 240

echo
echo "== SaaSAuthMiddleware focused symbols/usages =="
rg -n -C4 'SaaSAuthMiddleware|jwt|connection|companyId|tenant|internal|microservice|connectionId|Authorization|bearer|secret-access-log|secretAccess' backend/src -g '*.ts' | head -n 300

Repository: rocket-admin/rocketadmin

Length of output: 50380


IDOR (CWE-639): Authorization Bypass Through User-Controlled Key (IDOR)

Reachability: External

Reachability path
● Entry
  backend/src/microservices/sitenova-microservice/sitenova.module.ts:106
  SitenovaInternalController
│
▼
● Sink
  backend/src/microservices/sitenova-microservice/sitenova-internal.controller.ts

Add connection-level authorization to the SitNova connection-credentials endpoint.

SaaSAuthMiddleware only checks the microservice JWT and accepts request_id; it does not scope the request to a connection or tenant. getConnection() passes the caller-controlled connectionId directly to findAndDecryptConnection(), then returns decrypted credentials and the HS256 signing key. Require an explicit connections[*]/service authorization claim or a matching connection guard before returning connection secrets.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/src/microservices/sitenova-microservice/sitenova-internal.controller.ts`
around lines 49 - 53, The getConnection endpoint currently trusts the
caller-controlled connectionId and returns decrypted credentials without
connection-level authorization. Update getConnection and its authorization flow
to require a valid connections[*] or service authorization claim, or an
equivalent guard proving access to that specific connection, before invoking
getConnectionUseCase.execute and returning secrets; reject unauthorized or
mismatched connection requests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants