Skip to content

feat(permissions): cache invalidation для permission layer (#205)#217

Merged
NovakPAai merged 1 commit into
mainfrom
claude/novak-cache-invalidation-205
May 20, 2026
Merged

feat(permissions): cache invalidation для permission layer (#205)#217
NovakPAai merged 1 commit into
mainfrom
claude/novak-cache-invalidation-205

Conversation

@NovakPAai
Copy link
Copy Markdown
Owner

Closes #205. Tracker: #191. Depends on #192, #193 (both merged).

Что делает

Подключает существующие invalidateUser/Workspace/System к 9 мутационным точкам permissions API + Redis pub/sub для распространения invalidation между backend-инстансами.

Без этого PR: смена роли / permission override / feature toggle становится видимой через 5 минут (TTL).

Точки инжекции (9)

Файл Мутация Invalidate
permissions.service.ts toggleSystemFeature system
permissions.service.ts updateRolePreset (если permissions меняются) cascade user[]
permissions.service.ts setUserRole user
permissions.service.ts createUserPermission user
permissions.service.ts deleteUserPermission user
workspaces.service.ts toggleWorkspaceFeature workspace
workspaces.service.ts add/removeWorkspaceMemberPermission user
workspaces.service.ts updateMemberRole user
workspaces.service.ts addMember/inviteByEmail/removeMember user

Multi-instance pub/sub

Канал permissions:invalidate. Каждое сообщение содержит instanceId — подписчик skip'ает свои сообщения (no loop).

Безопасность:

  • 500ms debounce на type:system — защита от forge-DoS если злоумышленник пишет в Redis
  • redactUrl() чистит пароли из node-redis error messages перед логами
  • Race-safe subscriber init через in-flight promise singleton
  • Test-only helpers (__getCacheSize, __hasCacheEntry, __handleMessageForTests, __getInstanceIdForTests) throw в production

Graceful degradation: Redis недоступен → publish no-op (local invalidate всё равно работает; multi-instance восстановится через TTL).

Drive-by

Убраны 3 console.error дебаг-лога из #193 в rate-limit.ts (__reset, fallback) и error-handler.ts ([errorHandler 429]).

Review

Test plan

  • backend/src/__tests__/cache-invalidation.test.ts — 12 integration тестов
  • backend/src/__tests__/permissions-invalidate.test.ts — 16 unit тестов
  • Полный backend suite: 429/429 ✓
  • npm run typecheck
  • npm run lint ✓ (только pre-existing warnings)

Follow-ups

…205)

9 mutation points (admin + workspace) now broadcast invalidate:
- toggleSystemFeature, setUserRole, create/deleteUserPermission
- toggleWorkspaceFeature, add/removeWorkspaceMemberPermission
- updateMemberRole, addMember/inviteByEmail, removeMember
- updateRolePreset cascades to all preset holders (batched-100 + setImmediate)

New shared/utils/permissions-invalidate.ts:
- broadcastInvalidate{User,Workspace,System}: local + Redis publish
- subscriber with INSTANCE_ID skip-own (no pub/sub loop)
- 500ms debounce on system invalidates (forge-DoS guard, security review H-2)
- redactUrl() strips passwords from Redis error messages
- race-safe init with in-flight promise singleton
- test-only helpers throw in production

server.ts: subscriber init on listen, closeRedisClient + closeInvalidateSubscriber on SIGTERM/SIGINT, logger.info instead of console.log.

redis.ts: getRedisSubClient (separate sub connection), closeRedisClient,
on('error') logs error code only (no URL leak), orphan-promise suppression.

permissions-cache.ts: NUL-byte sentinel for global scope keys (avoids "null" string collision); __getCacheSize/__hasCacheEntry test helpers.

Drive-by: removed leftover debug console.error from #193 in rate-limit middleware and error-handler.

Tests:
- backend/src/__tests__/cache-invalidation.test.ts (12 integration)
- backend/src/__tests__/permissions-invalidate.test.ts (16 unit)
- Total: 429/429 backend tests passing

Closes #205. L-4 deferred follow-up (NODE_ENV=e2e bypass) filed as #216.
@NovakPAai NovakPAai merged commit d8a1724 into main May 20, 2026
8 checks passed
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.

[permissions-acl] Cache invalidation: подключить invalidate-функции к мутациям

2 participants