refactor: scope ApplicationRegistrationService findOneById to tenant rows (#20408)

## Summary

- `findOneById` is the lookup used by tenant-scoped operations
(`update`, `delete`, `rotateClientSecret`, `getStats`,
`transferOwnership`). It currently also matches `ownerWorkspaceId IS
NULL` rows, which was a leftover from when `ownerWorkspaceId` was made
nullable to support catalog-synced apps.
- System-level rows (marketplace catalog entries, the Twenty CLI
registration, dynamic OAuth client registrations) are already managed
through dedicated admin paths — `findAll()` and `findOneByIdGlobal()`
behind `AdminPanelGuard` — so the `IS NULL` fallback in `findOneById` is
unused by any real caller.
- Dropping it tightens the contract: tenant-scoped helpers operate on
tenant rows, global helpers operate on the global view. No behavior
change for any current legitimate flow.

## Test plan

- [ ] Existing application-registration GraphQL queries/mutations
(`findOneApplicationRegistration`, `updateApplicationRegistration`,
`deleteApplicationRegistration`,
`rotateApplicationRegistrationClientSecret`,
`findApplicationRegistrationStats`,
`transferApplicationRegistrationOwnership`) continue to work for a
workspace's own registrations.
- [ ] Admin Panel "Apps" tab continues to list and view all
registrations (uses `findAllApplicationRegistrations` /
`findOneAdminApplicationRegistration`, unaffected).
- [ ] Marketplace catalog sync still upserts catalog rows (uses
`findOneByUniversalIdentifier`, unaffected).
- [ ] Twenty CLI registration bootstrap still works (uses
`findOneByUniversalIdentifier`, unaffected).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Félix Malfait
2026-05-08 15:37:21 +02:00
committed by GitHub
parent 773245fa65
commit 23aa859502
@@ -6,7 +6,7 @@ import crypto from 'crypto';
import * as bcrypt from 'bcrypt';
import { type Manifest } from 'twenty-shared/application';
import { isDefined } from 'twenty-shared/utils';
import { IsNull, type Repository } from 'typeorm';
import { type Repository } from 'typeorm';
import { v4 } from 'uuid';
import { ALL_OAUTH_SCOPES } from 'src/engine/core-modules/application/application-oauth/constants/oauth-scopes';
@@ -61,10 +61,7 @@ export class ApplicationRegistrationService {
ownerWorkspaceId: string,
): Promise<ApplicationRegistrationEntity> {
const registration = await this.applicationRegistrationRepository.findOne({
where: [
{ id, ownerWorkspaceId },
{ id, ownerWorkspaceId: IsNull() },
],
where: { id, ownerWorkspaceId },
});
if (!registration) {