Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fix(seed): Handle missing permissions during database seeding#910

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
ArvindParekh wants to merge2 commits intostack-auth:dev
base:dev
Choose a base branch
Loading
fromArvindParekh:fix/team_permission

Conversation

@ArvindParekh
Copy link

@ArvindParekhArvindParekh commentedSep 22, 2025
edited by coderabbitaibot
Loading

Problem

Fixes#868. The problem was that database seeding fails withPermission "team_member" not found error in certain edge cases. This happens when the internal project record exists in the database but theteam_member andteam_admin permissions are missing from the project configuration. The seed script then skips permission creation (since the project exists) but still attempts to update the missing permissions, causing the error.

I'm not sure, but I guess this edge case can occur during Docker re-deployments with persistent databases, interrupted setup processes, or maybe database corruption/version mismatches.

Solution

I added a newensurePermissionDefinition() function inpermissions.tsx that safely handles both scenarios. If the permission exists, it updates it using the existingupdatePermissionDefinition() function. If the permission doesn't exist, it creates it using the existingcreatePermissionDefinition() function.

The seed scriptseed.ts now uses this new function for bothteam_member andteam_admin permissions instead of always trying to update them.

I thought of addingensurePermissionDefinition() as a helper function inseed.ts itself, since it has no usage anywhere else, but ultimately put it inpermissions.tsx, to keep all permission handling functions in one place. (Let me know if you'd like it otherwise)

High-level PR Summary

This PR fixes issue#868 where database seeding fails with a "Permission not found" error when the internal project exists but required permissions are missing. It introduces a newensurePermissionDefinition() function that safely handles both creating and updating permissions as needed. The seed script now uses this function instead of always attempting to update permissions, preventing failures in edge cases where permissions don't exist yet.

⏱️ Estimated Review Time: 5-15 minutes

💡 Review Order Suggestion
OrderFile Path
1apps/backend/src/lib/permissions.tsx
2apps/backend/prisma/seed.ts

Important

Fixes database seeding error by addingensurePermissionDefinition() to handle missing permissions inseed.ts.

  • Behavior:
  • Functions:
    • AddsensurePermissionDefinition() inpermissions.tsx to check and handle permission existence.
    • Utilizes existingupdatePermissionDefinition() andcreatePermissionDefinition() withinensurePermissionDefinition().
  • Misc:
    • Minor refactoring inseed.ts to use the new function for permission handling.

This description was created byEllipsis for8d6b9fa. You cancustomize this summary. It will automatically update as commits are pushed.

Review by RecurseML

🔍 Review performed on7a0bf86..8d6b9fa

  Severity    Location    Issue    Delete  
Mediumapps/backend/prisma/seed.ts:200Missing runAsynchronously wrapper for async function call
Mediumapps/backend/prisma/seed.ts:214Missing runAsynchronously wrapper for async function call
✅ Files analyzed, no issues (1)

apps/backend/src/lib/permissions.tsx

Need help? Join our Discord

Summary by CodeRabbit

  • New Features
    • Automatically creates missing permission definitions during setup so permissions are consistently available for teams and projects.
  • Bug Fixes
    • Prevents seeding errors by correcting permission identifier handling.
    • Preserves descriptions and nested permissions when updating existing definitions.
  • Chores
    • Streamlined backend permission-definition flow for improved reliability and maintainability.

greptile-apps[bot] reacted with thumbs up emojirecurseml[bot] reacted with heart emojiellipsis-dev[bot] reacted with rocket emoji
CopilotAI review requested due to automatic review settingsSeptember 22, 2025 18:19
@vercel
Copy link

vercelbot commentedSep 22, 2025

@ArvindParekh is attempting to deploy a commit to theStack Team onVercel.

A member of the Team first needs toauthorize it.

@coderabbitai
Copy link
Contributor

coderabbitaibot commentedSep 22, 2025
edited
Loading

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Replaced calls to updatePermissionDefinition with ensurePermissionDefinition in the seed script, adjusted payload shapes (useid, removeddata.id), and added an exported ensurePermissionDefinition helper in the permissions library (duplicate implementation present).

Changes

Cohort / File(s)Summary
Seeding script updates
apps/backend/prisma/seed.ts
Switched import fromupdatePermissionDefinition toensurePermissionDefinition; replaced calls to useid (instead ofoldId) and removeddata.id fields in payloads; kept other fields unchanged.
Permissions lib addition (duplication)
apps/backend/src/lib/permissions.tsx
Added and exportedensurePermissionDefinition(...) that checks existence and callsupdatePermissionDefinition orcreatePermissionDefinition accordingly (upsert-like). The implementation appears twice (duplicate).

Sequence Diagram(s)

sequenceDiagram  autonumber  participant Seed as Seed Script  participant PermLib as permissions.tsx  participant DB as Database  rect rgb(240,250,240)    Note over Seed,PermLib: Seed now calls ensurePermissionDefinition    Seed->>PermLib: ensurePermissionDefinition(scope, tenancy, id, data)  end  PermLib->>DB: getPermissionDefinition(id)  alt exists    PermLib->>DB: updatePermissionDefinition(id, data)    DB-->>PermLib: updated  else not found    PermLib->>DB: createPermissionDefinition(id, data)    DB-->>PermLib: created  end  PermLib-->>Seed: result  Note over PermLib: Implementation duplicated in file
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I thump my paws and check each row,
If no perm grows, I plant it slow;
If one exists, I trim its stem—
Two copies sprang up, a double gem!
I’ll hop to clean and make it flow. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check nameStatusExplanationResolution
Out of Scope Changes Check⚠️ WarningThe PR contains at least one out-of-scope or unintended change: apps/backend/src/lib/permissions.tsx appears to include the new ensurePermissionDefinition implementation twice (exact duplication), which looks accidental and unrelated to the stated objective. There are also payload/key changes in seed.ts (oldId → id and removal of data.id) that are not explained in the description and should be justified, and RecurseML flagged missing runAsynchronously wrappers for async calls in seed.ts; these unexplained items indicate changes beyond the core objective. Because of the duplicated implementation and unexplained payload/API mutations, this check fails.Please remove the duplicate ensurePermissionDefinition implementation, document or justify the payload key changes (or revert them if accidental), and address the missing runAsynchronously wrappers reported by automated analysis; provide a follow-up commit that fixes these issues before merging.
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.You can run@coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check nameStatusExplanation
Title Check✅ PassedThe title "Fix(seed): Handle missing permissions during database seeding" succinctly and accurately describes the primary change: making the seed flow tolerant of missing permission definitions. It is concise, follows conventional commit style, and directly matches the modifications in apps/backend/prisma/seed.ts and apps/backend/src/lib/permissions.tsx. The title contains no irrelevant noise and is clear to a reviewer scanning history.
Linked Issues Check✅ PassedThe changes implement ensurePermissionDefinition and replace updatePermissionDefinition calls in the seed script so missing permissions are created instead of causing a KnownError<PERMISSION_NOT_FOUND>, which directly satisfies the primary objective of linked issue#868. The core behavior change — create-if-missing or update-if-present for permission definitions — is present and exercised by the seed updates. Automated analysis flags (duplicate implementation and missing async wrappers) are worth fixing but do not prevent this PR from addressing the linked issue's main requirement.
Description Check✅ PassedThe pull request description includes the repository template header and provides a clear Problem and Solution section, references linked issue#868, and summarizes the high-level changes (adding ensurePermissionDefinition and updating the seed flow); it also includes the automated RECURSEML summary and contextual notes for reviewers. The description supplies the information needed to understand intent and impact for this change. Overall it is sufficiently complete for code review.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between8d6b9fa andd0c503d.

📒 Files selected for processing (1)
  • apps/backend/src/lib/permissions.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/backend/src/lib/permissions.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Security Check

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 and usage tips.

Copy link

CopilotAI left a comment

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 fixes a database seeding issue where missing permissions during seeding caused errors. The problem occurred when the internal project existed butteam_member andteam_admin permissions were missing from the configuration.

  • AddedensurePermissionDefinition() function that safely handles both creating and updating permissions
  • Updated seed script to use the new function instead of always attempting updates
  • Simplified parameter structure by removing redundantoldId and nestedid fields

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
apps/backend/src/lib/permissions.tsxAdded newensurePermissionDefinition() function that creates or updates permissions safely
apps/backend/prisma/seed.tsUpdated to useensurePermissionDefinition() instead ofupdatePermissionDefinition() for team permissions

Tip: Customize your code reviews with copilot-instructions.md.Create the file orlearn how to get started.

});
}else{
// permission doesn't exist, create it
returnawaitcreatePermissionDefinition(globalTx,{

Choose a reason for hiding this comment

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

ThecreatePermissionDefinition function expects asourceOfTruthTx parameter based on theupdatePermissionDefinition call above, but onlyglobalTx is being passed. This inconsistency could cause issues ifcreatePermissionDefinition requires the second transaction parameter.

Suggested change
returnawaitcreatePermissionDefinition(globalTx,{
returnawaitcreatePermissionDefinition(globalTx,sourceOfTruthTx,{

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

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

createPermissionDefinition actually doesn't require thesourceOfTruthTx parameter.

});

awaitupdatePermissionDefinition(
awaitensurePermissionDefinition(

Choose a reason for hiding this comment

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

This code violates the rule to properly handle asynchronous operations. TheensurePermissionDefinition function is async but the call is not wrapped withrunAsynchronously() as required by the code patterns rule. This can lead to unhandled promise rejections if the function fails.

🔍 This comment matches yourcode_patterns.mdc rule.

Suggested change
awaitensurePermissionDefinition(
awaitrunAsynchronously(ensurePermissionDefinition(

React with 👍 to tell me that this comment was useful, or 👎 if not (and I'll stop posting more comments like this in the future)

Copy link
Contributor

@greptile-appsgreptile-appsbot left a comment

Choose a reason for hiding this comment

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

Greptile Summary

This PR introduces a robust solution to fix database seeding failures that occur when permissions are missing from the project configuration. The core change adds a newensurePermissionDefinition() function inpermissions.tsx that handles both creation and update scenarios for permission definitions.

TheensurePermissionDefinition() function first checks if a permission exists in the tenancy configuration usinglistPermissionDefinitionsFromConfig(). If the permission exists, it calls the existingupdatePermissionDefinition() function. If the permission doesn't exist, it creates it usingcreatePermissionDefinition(). This defensive programming approach prevents the "Permission not found" errors that previously occurred when the internal project record existed but essential permissions liketeam_member andteam_admin were missing.

The seed script (seed.ts) is updated to use this new function instead of directly callingupdatePermissionDefinition(). The import statement is modified, and the function calls for bothteam_member andteam_admin permissions are updated to use the new ensure-style approach. The parameter structure is also adjusted - theoldId parameter is removed and the permissionid becomes a top-level parameter rather than being nested in thedata object.

This change addresses edge cases that can occur during Docker re-deployments with persistent databases, interrupted setup processes, or database corruption scenarios where the project exists but its permission definitions are incomplete.

Confidence score: 4/5

  • This PR is safe to merge with minimal risk of breaking existing functionality
  • Score reflects well-structured defensive programming and proper use of existing functions
  • Pay close attention to the parameter handling differences between create and update scenarios

2 files reviewed, 1 comment

Edit Code Review Bot Settings |Greptile

Comment on lines +366 to +374
returnawaitcreatePermissionDefinition(globalTx,{
scope:options.scope,
tenancy:options.tenancy,
data:{
id:options.id,
description:options.data.description,
contained_permission_ids:options.data.contained_permission_ids,
},
});
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: Inconsistent transaction parameter usage between create and update operations.createPermissionDefinition only receivesglobalTx, whileupdatePermissionDefinition receives bothglobalTx andsourceOfTruthTx.

Copy link
Contributor

@coderabbitaicoderabbitaibot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/backend/prisma/seed.ts (1)

361-366:Bug: stale tenancy used when granting team_admin.

grantTeamPermission validates existence against tenancy.config. After creating permissions, you reloaded updatedInternalTenancy, but this call still uses internalTenancy, which may not include the new definition and can throw PermissionNotFound.

Apply:

-    await grantTeamPermission(internalPrisma, {-      tenancy: internalTenancy,+    await grantTeamPermission(internalPrisma, {+      tenancy: updatedInternalTenancy,       teamId: internalTeamId,       userId: defaultUserId,       permissionId: "team_admin",     });
🧹 Nitpick comments (3)
apps/backend/src/lib/permissions.tsx (1)

337-376:Tidy: avoid rename path and redundant awaits.

Passing data.id equals oldId needlessly activates rename logic; also return await is unnecessary here.

  • Drop data.id in the update branch (already covered in the diff above).
  • Keep returns without await (already covered above).
apps/backend/prisma/seed.ts (2)

200-211:Good switch to ensurePermissionDefinition; improve description text.

Logic is correct. Consider replacing placeholder descriptions ("1") with meaningful, user-facing text.

Suggested tweak:

       data: {-        description: "1",+        description: "Team member role",         contained_permission_ids: ["$read_members"],       }

214-226:Same here: swap placeholder description.

Use a clear description for team_admin.

       data: {-        description: "2",+        description: "Team admin role",         contained_permission_ids: ["$read_members", "$remove_members", "$update_team"],       }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between7a0bf86 and8d6b9fa.

📒 Files selected for processing (2)
  • apps/backend/prisma/seed.ts (2 hunks)
  • apps/backend/src/lib/permissions.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer ES6 Map over Record when representing key–value collections

Files:

  • apps/backend/src/lib/permissions.tsx
  • apps/backend/prisma/seed.ts
🧬 Code graph analysis (2)
apps/backend/src/lib/permissions.tsx (2)
apps/backend/src/lib/types.tsx (1)
  • PrismaTransaction (3-3)
apps/backend/src/lib/tenancies.tsx (1)
  • Tenancy (47-47)
apps/backend/prisma/seed.ts (3)
apps/backend/src/lib/permissions.tsx (1)
  • ensurePermissionDefinition (337-376)
apps/backend/src/prisma-client.tsx (1)
  • globalPrismaClient (33-33)
apps/backend/src/lib/tenancies.tsx (2)
  • getSoleTenancyFromProjectBranch (59-66)
  • DEFAULT_BRANCH_ID (17-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Security Check
🔇 Additional comments (1)
apps/backend/prisma/seed.ts (1)

4-4:Import change looks good.

Switching to ensurePermissionDefinition aligns seed with upsert behavior.

Comment on lines 337 to 376
exportasyncfunctionensurePermissionDefinition(
globalTx:PrismaTransaction,
sourceOfTruthTx:PrismaTransaction,
options:{
scope:"team"|"project",
tenancy:Tenancy,
id:string,
data:{
description?:string,
contained_permission_ids?:string[],
},
}
){
constexistingPermission=options.tenancy.config.rbac.permissions[options.id];

if(existingPermission){
// permission already exists, update it
returnawaitupdatePermissionDefinition(globalTx,sourceOfTruthTx,{
scope:options.scope,
tenancy:options.tenancy,
oldId:options.id,
data:{
id:options.id,
description:options.data.description,
contained_permission_ids:options.data.contained_permission_ids,
},
});
}else{
// permission doesn't exist, create it
returnawaitcreatePermissionDefinition(globalTx,{
scope:options.scope,
tenancy:options.tenancy,
data:{
id:options.id,
description:options.data.description,
contained_permission_ids:options.data.contained_permission_ids,
},
});
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent cross-scope updates in ensurePermissionDefinition.

If an existing permission has a different scope than options.scope, this path will silently flip its scope via updatePermissionDefinition. That’s risky for RBAC integrity. Add an explicit scope guard and fail fast.

Apply this diff:

@@ export async function ensurePermissionDefinition(   globalTx: PrismaTransaction,   sourceOfTruthTx: PrismaTransaction,   options: {     scope: "team" | "project",     tenancy: Tenancy,     id: string,     data: {       description?: string,       contained_permission_ids?: string[],     },   } ) {-  const existingPermission = options.tenancy.config.rbac.permissions[options.id];+  const existingPermission = options.tenancy.config.rbac.permissions[options.id] as CompleteConfig['rbac']['permissions'][string] | undefined;++  // Do not allow cross-scope edits.+  if (existingPermission && (existingPermission.scope ?? null) !== options.scope) {+    throw new KnownErrors.PermissionScopeMismatch(options.id, options.scope, existingPermission.scope ?? null);+  }@@-    return await updatePermissionDefinition(globalTx, sourceOfTruthTx, {+    return updatePermissionDefinition(globalTx, sourceOfTruthTx, {       scope: options.scope,       tenancy: options.tenancy,       oldId: options.id,       data: {-        id: options.id,         description: options.data.description,         contained_permission_ids: options.data.contained_permission_ids,       },     });   } else {     // permission doesn't exist, create it-    return await createPermissionDefinition(globalTx, {+    return createPermissionDefinition(globalTx, {       scope: options.scope,       tenancy: options.tenancy,       data: {         id: options.id,         description: options.data.description,         contained_permission_ids: options.data.contained_permission_ids,       },     });   } }
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exportasyncfunctionensurePermissionDefinition(
globalTx:PrismaTransaction,
sourceOfTruthTx:PrismaTransaction,
options:{
scope:"team"|"project",
tenancy:Tenancy,
id:string,
data:{
description?:string,
contained_permission_ids?:string[],
},
}
){
constexistingPermission=options.tenancy.config.rbac.permissions[options.id];
if(existingPermission){
// permission already exists, update it
returnawaitupdatePermissionDefinition(globalTx,sourceOfTruthTx,{
scope:options.scope,
tenancy:options.tenancy,
oldId:options.id,
data:{
id:options.id,
description:options.data.description,
contained_permission_ids:options.data.contained_permission_ids,
},
});
}else{
// permission doesn't exist, create it
returnawaitcreatePermissionDefinition(globalTx,{
scope:options.scope,
tenancy:options.tenancy,
data:{
id:options.id,
description:options.data.description,
contained_permission_ids:options.data.contained_permission_ids,
},
});
}
}
exportasyncfunctionensurePermissionDefinition(
globalTx:PrismaTransaction,
sourceOfTruthTx:PrismaTransaction,
options:{
scope:"team"|"project",
tenancy:Tenancy,
id:string,
data:{
description?:string,
contained_permission_ids?:string[],
},
}
){
constexistingPermission=options.tenancy.config.rbac.permissions[options.id]asCompleteConfig['rbac']['permissions'][string]|undefined;
// Do not allow cross-scope edits.
if(existingPermission&&(existingPermission.scope??null)!==options.scope){
thrownewKnownErrors.PermissionScopeMismatch(options.id,options.scope,existingPermission.scope??null);
}
if(existingPermission){
// permission already exists, update it
returnupdatePermissionDefinition(globalTx,sourceOfTruthTx,{
scope:options.scope,
tenancy:options.tenancy,
oldId:options.id,
data:{
description:options.data.description,
contained_permission_ids:options.data.contained_permission_ids,
},
});
}else{
// permission doesn't exist, create it
returncreatePermissionDefinition(globalTx,{
scope:options.scope,
tenancy:options.tenancy,
data:{
id:options.id,
description:options.data.description,
contained_permission_ids:options.data.contained_permission_ids,
},
});
}
}
🤖 Prompt for AI Agents
In apps/backend/src/lib/permissions.tsx around lines 337 to 376, the currentlogic will allow an existing permission with a different scope to be updated tooptions.scope, which can silently flip scopes; add an explicit guard thatcompares existingPermission.scope to options.scope and throw a clear error (orreturn a failed Promise) if they differ so the function fails fast instead ofcalling updatePermissionDefinition, ensuring no cross-scope updates occur.

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

Reviewers

@coderabbitaicoderabbitai[bot]coderabbitai[bot] left review comments

Copilot code reviewCopilotCopilot left review comments

+2 more reviewers

@greptile-appsgreptile-apps[bot]greptile-apps[bot] left review comments

@recursemlrecurseml[bot]recurseml[bot] left review comments

Reviewers whose approvals may not affect merge requirements

At least 1 approving review is required to merge this pull request.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Permission "team_member" not found. Make sure you created it on the dashboard.

1 participant

@ArvindParekh

[8]ページ先頭

©2009-2025 Movatter.jp