| 218 | export const AUTHORIZE_ORGANIZATION_SPAN = "auth.authorize_organization"; |
| 219 | |
| 220 | export const authorizeOrganization = ( |
| 221 | userId: string, |
| 222 | organizationId: string, |
| 223 | options: AuthorizeOrganizationOptions = {}, |
| 224 | ) => |
| 225 | Effect.gen(function* () { |
| 226 | const org = yield* heldOrResolvedForMember(userId, organizationId); |
| 227 | if (!org) return null; |
| 228 | // An unmarked live organization is scanned before its mirror is read |
| 229 | // (see above). The row returned below still shows the mark as it was |
| 230 | // read; nothing past this point reads it. A marked-deleted organization |
| 231 | // is never scanned, so the deletion retry (`deleted: "allow"`) reaches |
| 232 | // `authorized()` below with the membership row the purge has not removed |
| 233 | // yet. |
| 234 | if (org.deletedAt === null && org.backfilledAt === null) { |
| 235 | yield* ensureOrganizationBackfilled(organizationId); |
| 236 | } |
| 237 | const membership = yield* activeMembershipFromMirror(userId, organizationId); |
| 238 | if (!membership) return null; |
| 239 | return authorized(org, membership, options); |
| 240 | }).pipe(Effect.withSpan(AUTHORIZE_ORGANIZATION_SPAN)); |
| 241 | |
| 242 | // --------------------------------------------------------------------------- |
| 243 | // Org SELECTOR — the URL is the scope authority, not the session. |