diff --git a/README.md b/README.md index 275f1c1..ea26da8 100644 --- a/README.md +++ b/README.md @@ -187,8 +187,8 @@ changing either without seeing what already exists makes no sense. The other imp are seeded defaults you can edit. The permissions that other PoliNetwork services check during the IdP–Telegram migration -(`admin:access`, `tg:*`, `wa:groups:manage`, `groups:labels:write`, `web:*` and -`azure:members:create`, see [RFC §4.1](docs/idp-telegram-integration-rfc-v3.md)) are seeded +(`admin:access`, `tg:*`, `wa:groups:manage`, `groups:labels:write`, `web:*`, +`azure:members:create` and `azure:members:write`, see [RFC §4.1](docs/idp-telegram-integration-rfc-v3.md)) are seeded as ordinary permissions, with `tg:immune` granting `tg:trusted`. No role is given them: Master Admin holds them through its wildcard, and administrators choose which roles grant them. diff --git a/docs/idp-telegram-integration-rfc-v3.md b/docs/idp-telegram-integration-rfc-v3.md index fea5fa6..ca446ae 100644 --- a/docs/idp-telegram-integration-rfc-v3.md +++ b/docs/idp-telegram-integration-rfc-v3.md @@ -158,7 +158,8 @@ These are agreed. Reviewers should check their consequences, not reopen them. - Existing grants are **not migrated**. The table is recreated, and any needed grants are re-entered by hand after cutover. 7. **Delegated administration separates editing from assignment.** `idp:roles:write` edits role definitions; `idp:roles:assign` assigns or revokes roles. Neither implies the other. Both use the IdP's bounded-delegation rules: no managed-role assignment, no assignment beyond the actor's effective permissions, and no self-escalation. The current combined endpoint checks must be split during implementation. 8. **Ban-all is authorized once, when it is queued.** A started ban-all runs to completion, and revocation does not stop it. Queue redesign is out of scope; the queue will move to the backend later. -9. **The dashboard can read members and create a socio.** A dedicated permission authorizes a backend workflow that creates an Entra user and adds only that newly created user to the fixed Soci group. The dedicated `azure:members:read` permission permits read-only browsing of members, independently from `azure:members:create`. The dashboard cannot edit existing Entra users or browse or edit groups, and cannot choose a group ID or existing user ID for this workflow. There is no `AZURE_WRITABLE_GROUPS` dashboard policy. +9. **The dashboard can read members, create a socio and change a member number.** A dedicated permission authorizes a backend workflow that creates an Entra user and adds only that newly created user to the fixed Soci group. The dedicated `azure:members:read` permission permits read-only browsing of members, and `azure:members:write` permits changing the member number (Entra `employeeId`) of an existing user, optionally emailing them the new number. None of `azure:members:read`, `azure:members:create` and `azure:members:write` implies another. Apart from the member number, the dashboard cannot edit existing Entra users or browse or edit groups, and cannot choose a group ID or existing user ID for the creation workflow. There is no `AZURE_WRITABLE_GROUPS` dashboard policy. + - _Amended after revision 3:_ this decision originally said the dashboard cannot edit existing Entra users. Changing the member number was reintroduced deliberately, with its own permission, and is the only such edit. 10. **Master Admin holds the entire permission catalog.** This includes all current and future Telegram permissions, with no namespace exclusion or scoped variant. 11. **Role grants are runtime configuration.** Administrators choose and change which permissions each role grants in the IdP. No service authorizes by role name. The migration does not prescribe Web Team or HR Telegram grants. @@ -166,31 +167,32 @@ These are agreed. Reviewers should check their consequences, not reopen them. Permissions follow the convention `:[:]`. This is the frozen migration catalog; grants to roles remain configurable at runtime (§4.2). -| Permission | Meaning | Replaces | -| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | -| `admin:access` | Can open the dashboard | `ADMIN_ROLES` | -| `tg:moderate` | ban/tban/kick/mute/tmute/unban/unmute in any network chat; report-menu Kick/Ban | owner/direttivo moderation | -| `tg:moderate:global` | `/ban_all`, `/unban_all`, report-menu "Start BAN ALL", campaign review Confirm/Release | owner/direttivo | -| `tg:messages:delete` | `/del`, report-menu Del | `admin`, owner, direttivo | -| `tg:messages:pin` | `/pin`, `/unpin` | direttivo, owner | -| `tg:immune` | Ban-all target immunity; campaign-spam exemption. **Implies `tg:trusted`.** | `BYPASS_ROLES`, `NETWORK_PRIVILEGED_ROLES` | -| `tg:trusted` | Message trust (auto-moderation whitelist), hashtag-rule exemption | "has any role" checks | -| `tg:audit:read` | `/audit`, audit history in the dashboard | hr, owner, direttivo | -| `tg:users:read` | Telegram user list and details, `/userid` | `ADMIN_ROLES` | -| `tg:messages:read` | Read stored (decrypted) messages in the dashboard | `ADMIN_ROLES` (today, unchecked) | -| `tg:groups:manage` | `/updategroup`, `/regenerate_group_links`, hide or leave a group | owner, direttivo, `web` (group write) | -| `tg:bot:add` | May add the bot to a group | `CAN_ADD_BOT` | -| `tg:grants:read` | View grants | `ADMIN_ROLES` | -| `tg:grants:manage` | Create and interrupt grants | `CAN_MANAGE_GRANTS` | -| `wa:groups:manage` | WhatsApp group CRUD | group write roles | -| `groups:labels:write` | Group label CRUD and tagging | web write roles | -| `web:content:write` | Associations, FAQs, projects, freshman guides | web write roles | -| `web:reports:manage` | Resolve or dismiss group-link reports | group write roles | -| `azure:members:read` | Read the Microsoft 365 member directory without changing users or groups. | Dedicated read capability | -| `azure:members:create` | Create a new socio through the backend's fixed Entra workflow. Includes adding only the newly created user to Soci; no arbitrary group or user editing. | New dedicated capability | -| `idp:roles:assign` | Assign or revoke an unmanaged role within bounded delegation; independent of `idp:roles:write` | Assignment half of existing `idp:roles:write` | - -Existing IdP permissions remain in the catalog: `membership:read`, `student:verified`, `idp:people:read`, `idp:users:read`, `idp:users:delete`, `idp:permissions:read`, `idp:permissions:write`, `idp:roles:read`, `idp:roles:write`, `idp:applications:read` and `idp:applications:write`. `idp:roles:write` now authorizes role-definition changes only. `idp:roles:assign` implies `idp:roles:read` and `idp:people:read` so its holder can identify the role and person, but does not imply `idp:roles:write`; write does not imply assign. Review existing role writers before cutover and grant assignment separately where intended. `azure:members:write` and `azure:groups:write` are excluded from this migration catalog. +| Permission | Meaning | Replaces | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| `admin:access` | Can open the dashboard | `ADMIN_ROLES` | +| `tg:moderate` | ban/tban/kick/mute/tmute/unban/unmute in any network chat; report-menu Kick/Ban | owner/direttivo moderation | +| `tg:moderate:global` | `/ban_all`, `/unban_all`, report-menu "Start BAN ALL", campaign review Confirm/Release | owner/direttivo | +| `tg:messages:delete` | `/del`, report-menu Del | `admin`, owner, direttivo | +| `tg:messages:pin` | `/pin`, `/unpin` | direttivo, owner | +| `tg:immune` | Ban-all target immunity; campaign-spam exemption. **Implies `tg:trusted`.** | `BYPASS_ROLES`, `NETWORK_PRIVILEGED_ROLES` | +| `tg:trusted` | Message trust (auto-moderation whitelist), hashtag-rule exemption | "has any role" checks | +| `tg:audit:read` | `/audit`, audit history in the dashboard | hr, owner, direttivo | +| `tg:users:read` | Telegram user list and details, `/userid` | `ADMIN_ROLES` | +| `tg:messages:read` | Read stored (decrypted) messages in the dashboard | `ADMIN_ROLES` (today, unchecked) | +| `tg:groups:manage` | `/updategroup`, `/regenerate_group_links`, hide or leave a group | owner, direttivo, `web` (group write) | +| `tg:bot:add` | May add the bot to a group | `CAN_ADD_BOT` | +| `tg:grants:read` | View grants | `ADMIN_ROLES` | +| `tg:grants:manage` | Create and interrupt grants | `CAN_MANAGE_GRANTS` | +| `wa:groups:manage` | WhatsApp group CRUD | group write roles | +| `groups:labels:write` | Group label CRUD and tagging | web write roles | +| `web:content:write` | Associations, FAQs, projects, freshman guides | web write roles | +| `web:reports:manage` | Resolve or dismiss group-link reports | group write roles | +| `azure:members:read` | Read the Microsoft 365 member directory without changing users or groups. | Dedicated read capability | +| `azure:members:create` | Create a new socio through the backend's fixed Entra workflow. Includes adding only the newly created user to Soci; no arbitrary group or user editing. | New dedicated capability | +| `azure:members:write` | Change the member number of an existing Microsoft 365 user and optionally email them the new number. No other user or group changes. | Legacy `setAssocNumber` path; added after revision 3 (§4 decision 9) | +| `idp:roles:assign` | Assign or revoke an unmanaged role within bounded delegation; independent of `idp:roles:write` | Assignment half of existing `idp:roles:write` | + +Existing IdP permissions remain in the catalog: `membership:read`, `student:verified`, `idp:people:read`, `idp:users:read`, `idp:users:delete`, `idp:permissions:read`, `idp:permissions:write`, `idp:roles:read`, `idp:roles:write`, `idp:applications:read` and `idp:applications:write`. `idp:roles:write` now authorizes role-definition changes only. `idp:roles:assign` implies `idp:roles:read` and `idp:people:read` so its holder can identify the role and person, but does not imply `idp:roles:write`; write does not imply assign. Review existing role writers before cutover and grant assignment separately where intended. `azure:groups:write` is excluded from this migration catalog. `azure:members:write` was excluded too, then added after revision 3 for member-number changes only (§4 decision 9). The three `azure:members:*` permissions are independent; because the dashboard finds the row to change through the member directory, a holder of `azure:members:write` in practice also needs `azure:members:read`. ### 4.2 Runtime role policy and migration of old holders @@ -816,23 +818,24 @@ Every tRPC procedure, HTTP route and socket event is listed. **Anything not list ### 8.2 Groups, WhatsApp, web, Azure, misc -| Procedure | Actor | Scope | Permission | Notes | -| ------------------------------------------------------------------------------------------------------------ | ------- | --------------------------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `groups.labels.getAll` | U | `backend:admin` | `admin:access` | | -| `groups.labels.create`, `modify`, `delete`, `tagGroup`, `untagGroup` | U | `backend:admin` | `groups:labels:write` | `createdBy` / `updatedBy` come from `ctx`. | -| `groups.search.getAll`, `search`, `getById`, `getByInviteLink` | U | `backend:admin` | `admin:access` | | -| `wa.groups.getAll`, `getById`, `getByInviteLink` | U | `backend:admin` | `admin:access` | | -| `wa.groups.add`, `modify`, `delete`, `setHide` | U | `backend:admin` | `wa:groups:manage` | | -| `web.*.getAll*`, `web.guides_matricole.getLatestGuide` | web / U | `backend:public:read` / `backend:admin` | — / `admin:access` | | -| `web.associations.*`, `web.faqs.*`, `web.projects.*`, `web.guides_matricole.*` (writes) | U | `backend:admin` | `web:content:write` | `createdBy` / `modifiedBy` come from `ctx`. | -| `web.reports.create` | web | `backend:public:read` | — | Public form; add rate limiting. No current caller was found. | -| `web.reports.list`, `resolve`, `dismiss` | U | `backend:admin` | `web:reports:manage` | | -| `azure.members.create` | U | `backend:admin` | `azure:members:create` | Backend creates the Entra user, sets the profile and membership number, adds only that newly created user to the fixed Soci group and assigns licenses. Validate inputs, bind the group addition to the new user and audit the operation. No caller-supplied group or existing user ID. | -| `azure.members.getAll` | U | `backend:admin` | `azure:members:read` | Read-only member directory; independent from creation. | -| `azure.groups.getAll`, `azure.members.setAssocNumber`, `azure.groups.addMember`, `azure.groups.removeMember` | — | — | — | _Remove from the migrated dashboard._ These legacy paths get no dashboard permission in the new model; deny them after the caller cutover. | -| `me.access` _(new)_ | U | `backend:admin` | — | The caller's own permissions, from the snapshot. Drives the dashboard UI. | -| `auth.updateProfilePic` | — | — | — | _Remove._ Profiles live in the IdP. | -| `test.dbQuery`, `test.dbInsert` | — | — | — | _Remove_ (or mount only when `NODE_ENV !== "production"`). | +| Procedure | Actor | Scope | Permission | Notes | +| --------------------------------------------------------------------------------------- | ------- | --------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `groups.labels.getAll` | U | `backend:admin` | `admin:access` | | +| `groups.labels.create`, `modify`, `delete`, `tagGroup`, `untagGroup` | U | `backend:admin` | `groups:labels:write` | `createdBy` / `updatedBy` come from `ctx`. | +| `groups.search.getAll`, `search`, `getById`, `getByInviteLink` | U | `backend:admin` | `admin:access` | | +| `wa.groups.getAll`, `getById`, `getByInviteLink` | U | `backend:admin` | `admin:access` | | +| `wa.groups.add`, `modify`, `delete`, `setHide` | U | `backend:admin` | `wa:groups:manage` | | +| `web.*.getAll*`, `web.guides_matricole.getLatestGuide` | web / U | `backend:public:read` / `backend:admin` | — / `admin:access` | | +| `web.associations.*`, `web.faqs.*`, `web.projects.*`, `web.guides_matricole.*` (writes) | U | `backend:admin` | `web:content:write` | `createdBy` / `modifiedBy` come from `ctx`. | +| `web.reports.create` | web | `backend:public:read` | — | Public form; add rate limiting. No current caller was found. | +| `web.reports.list`, `resolve`, `dismiss` | U | `backend:admin` | `web:reports:manage` | | +| `azure.members.create` | U | `backend:admin` | `azure:members:create` | Backend creates the Entra user, sets the profile and membership number, adds only that newly created user to the fixed Soci group and assigns licenses. Validate inputs, bind the group addition to the new user and audit the operation. No caller-supplied group or existing user ID. The caller chooses the mailbox: `{ kind: "standard" }` builds `firstname.lastname@polinetwork.org` (accents and apostrophes stripped, spaces removed within each name); `{ kind: "custom", localPart }` takes lowercase letters, digits, dots and hyphens. The domain is always `polinetwork.org`. | +| `azure.members.getAll` | U | `backend:admin` | `azure:members:read` | Read-only member directory; independent from creation. | +| `azure.members.setAssocNumber` | U | `backend:admin` | `azure:members:write` | Input: `userId`, a positive integer `assocNumber` and an optional `notifyEmail` address that receives the new number. Refuse a number already held by another user, change nothing else on the user, and audit the change with the actor's `sub` and client. Added after revision 3 (§4 decision 9); no longer a legacy path. | +| `azure.groups.getAll`, `azure.groups.addMember`, `azure.groups.removeMember` | — | — | — | _Remove from the migrated dashboard._ These legacy paths get no dashboard permission in the new model; deny them after the caller cutover. | +| `me.access` _(new)_ | U | `backend:admin` | — | The caller's own permissions, from the snapshot. Drives the dashboard UI. | +| `auth.updateProfilePic` | — | — | — | _Remove._ Profiles live in the IdP. | +| `test.dbQuery`, `test.dbInsert` | — | — | — | _Remove_ (or mount only when `NODE_ENV !== "production"`). | ### 8.3 HTTP routes and socket.io @@ -859,7 +862,7 @@ Every tRPC procedure, HTTP route and socket event is listed. **Anything not list - **Snapshot client:** poll, events, persistence, validation and atomic swap (§5). - **`ctx.actor` and `ctx.can()`;** every procedure goes through `policy()` (§8). - **Actor fields removed from every input:** `adderId`, `removerId`, `interruptedById`, `performerId`, `adminId`, `createdBy`, `modifiedBy`, `updatedBy`, and the self `userId` of `auth.updateProfilePic`. -- **Azure creation boundary:** expose only `azure.members.create` to the migrated dashboard. The backend uses a fixed Soci group ID internally and adds only the user it just created. There is no dashboard group-write allowlist or arbitrary group-member API in the new model. During the compatibility phase, legacy anonymous callers stay on the logged legacy path until their cutover (§13). +- **Azure creation boundary:** expose only `azure.members.getAll` to the migrated dashboard for read-only browsing (`azure:members:read`), `azure.members.create` for creation, and `azure.members.setAssocNumber` for changing an existing user's member number. The backend uses a fixed Soci group ID internally and adds only the user it just created. There is no dashboard group-write allowlist or arbitrary group-member API in the new model. During the compatibility phase, legacy anonymous callers stay on the logged legacy path until their cutover (§13). ### 9.2 Audit log @@ -1101,7 +1104,7 @@ The principle: **each caller moves independently, and each step can be rolled ba **Phase 2: people and roles in the IdP (can run in parallel with Phase 3).** -1. Create the permissions from §4.1, including `idp:roles:assign`, `azure:members:read` and `azure:members:create`; implement the IdP's split role checks before assigning delegates. Review current `idp:roles:write` holders and grant `idp:roles:assign` separately where assignment should continue. Create whichever unmanaged roles administrators choose. No predefined role grant matrix is required. +1. Create the permissions from §4.1, including `idp:roles:assign`, `azure:members:read`, `azure:members:create` and `azure:members:write`; implement the IdP's split role checks before assigning delegates. Review current `idp:roles:write` holders and grant `idp:roles:assign` separately where assignment should continue. Create whichever unmanaged roles administrators choose. No predefined role grant matrix is required. 2. Export the current `tg_permissions` and `tg_group_admins` holders. 3. Each holder logs into the IdP and links Telegram through the official login. **Old links are never imported.** 4. Configure grants and assign roles by hand under §4.2's runtime policy. Conflicts are reviewed by hand. @@ -1114,7 +1117,7 @@ The principle: **each caller moves independently, and each step can be rolled ba - the new procedures (`tg.access.resolve`, `me.access`, `tg.auditLog.record`); - the new audit columns, the new grants table and the `/internal/events` endpoint; - socket token authentication, accepted when present; - - the dedicated `azure.members.create` policy and denial of other Azure dashboard operations for token-bearing callers. + - the dedicated `azure.members.getAll`, `azure.members.create` and `azure.members.setAssocNumber` policies and denial of other Azure dashboard operations for token-bearing callers. - Requests with a token are fully enforced, with actor fields ignored. Anonymous requests take the legacy path and are logged. - Publish the additive SDK. @@ -1207,16 +1210,16 @@ Beyond step 1's configuration, the IdP needs no code changes. The owner confirmed these decisions on 8 October 2026. They settle the migration contract; role grants themselves remain runtime configuration. -| Question | Decision | -| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Q1 — Master Admin | The existing catalog-wide wildcard includes all current and future `tg:*` permissions. No scoped Master Admin or namespace exception. | -| Q2 — Delegation | Separate `idp:roles:assign` from `idp:roles:write`. Assignment/revocation and definition editing remain independently grantable and bounded by effective authority. | -| Q3 — Freshness | `MAX_STALE = 1 h` and `ENTRA_GRACE = 1 h`. | -| Q4 — Native admins | Keep precisely the current `allowGroupAdmins` command set as the bot's chat-local shortcut (§10.2). | -| Q5 — Service identity | Use IdP client credentials for service API calls, including backend → IdP snapshot pulls. IdP → backend change notices use IdP-signed security-event tokens (§5.6). Neither path uses Kubernetes ServiceAccount tokens as application authentication. | -| Q6 — Bot lookups | The bot obtains access decisions through the backend. No direct IdP permission lookup or bot-side permission cache. | -| Q7 — Azure | The dashboard may create a socio through one dedicated backend operation and permission. The backend's fixed workflow adds the newly created user to Soci. No dashboard Azure browsing, existing-user editing or arbitrary group membership changes. | -| Q8 — Role grants | Administrators choose and change role permissions at runtime. The RFC does not hardcode Web Team, HR or other role grant lists. | +| Question | Decision | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Q1 — Master Admin | The existing catalog-wide wildcard includes all current and future `tg:*` permissions. No scoped Master Admin or namespace exception. | +| Q2 — Delegation | Separate `idp:roles:assign` from `idp:roles:write`. Assignment/revocation and definition editing remain independently grantable and bounded by effective authority. | +| Q3 — Freshness | `MAX_STALE = 1 h` and `ENTRA_GRACE = 1 h`. | +| Q4 — Native admins | Keep precisely the current `allowGroupAdmins` command set as the bot's chat-local shortcut (§10.2). | +| Q5 — Service identity | Use IdP client credentials for service API calls, including backend → IdP snapshot pulls. IdP → backend change notices use IdP-signed security-event tokens (§5.6). Neither path uses Kubernetes ServiceAccount tokens as application authentication. | +| Q6 — Bot lookups | The bot obtains access decisions through the backend. No direct IdP permission lookup or bot-side permission cache. | +| Q7 — Azure | The dashboard may create a socio through one dedicated backend operation and permission. The backend's fixed workflow adds the newly created user to Soci. No dashboard Azure browsing, existing-user editing or arbitrary group membership changes. Later amended to allow member-number changes (§4 decision 9). | +| Q8 — Role grants | Administrators choose and change role permissions at runtime. The RFC does not hardcode Web Team, HR or other role grant lists. | --- diff --git a/docs/idp-telegram-phase0.md b/docs/idp-telegram-phase0.md index 08ef36a..725cc6a 100644 --- a/docs/idp-telegram-phase0.md +++ b/docs/idp-telegram-phase0.md @@ -120,6 +120,8 @@ The owner answered Q1–Q8 on 8 October 2026. These are design decisions for the | Add `azure:members:create` | Authorizes the single create-socio workflow through the backend. It does not authorize arbitrary user updates or group operations. | | Remove proposed dashboard Azure permissions | Do not introduce `azure:members:read`, `azure:members:write` or `azure:groups:write` for the dashboard migration. The create-socio workflow is the only dashboard Azure capability in scope. | +_Later amendment:_ `azure:members:write` was introduced after this phase, deliberately amending RFC v3 §4 decision 9, to let the dashboard change the member number of an existing user through `azure.members.setAssocNumber`. It authorizes no other user or group change. The tables in this section are kept as agreed in Phase 0. + The create-socio workflow includes the necessary Entra user creation, profile and membership-number setup, addition of **that newly created user** to the fixed Soci group, and license assignment. The existing backend `createMember` code performs these steps; its Soci-group addition is an internal part of this one operation, not a general group-write permission. The backend must bind that addition to the user it just created and must not accept an arbitrary group ID or existing user ID for this operation. Existing `setAssocNumber`, group membership mutation and Azure browsing procedures are outside the dashboard's migration contract. **Role mapping (§4.2).** Treat the RFC's old-role mapping as migration context, not a fixed grant matrix. The IdP remains the sole source of global permissions. Administrators configure role grants in the IdP and may change them at runtime; no backend, bot or dashboard authorization check may depend on a role name. Managed-role membership and the Master Admin wildcard retain their existing IdP semantics. Before each caller is cut over, configure and review the initial grants needed for its operations; no Web Team or HR Telegram grant is prescribed here. diff --git a/drizzle/0020_azure_members_write_permission.sql b/drizzle/0020_azure_members_write_permission.sql new file mode 100644 index 0000000..ada0cff --- /dev/null +++ b/drizzle/0020_azure_members_write_permission.sql @@ -0,0 +1,9 @@ +-- Changing the member number (Entra `employeeId`) of an existing socio returns to the admin +-- dashboard, amending RFC v3 decision 9. The backend's `azure.members.setAssocNumber` checks +-- this key, so seeding it keeps every environment spelled the same. It neither implies nor is +-- implied by `azure:members:read` or `azure:members:create`. No role grant is seeded; Master +-- Admin covers it through its wildcard. A permission an administrator already created by hand +-- is left untouched. +INSERT INTO "permission" ("id", "key", "name", "description", "managed") VALUES + ('migration-permission-azure-members-write', 'azure:members:write', 'Change member numbers', 'Change the member number of an existing Microsoft 365 user and optionally email them the new number. No other user or group changes.', false) +ON CONFLICT ("key") DO NOTHING; diff --git a/drizzle/meta/0020_snapshot.json b/drizzle/meta/0020_snapshot.json new file mode 100644 index 0000000..0c3b6e6 --- /dev/null +++ b/drizzle/meta/0020_snapshot.json @@ -0,0 +1,2469 @@ +{ + "id": "8fd5a7a5-1395-4c02-a58e-27ec9ba987ae", + "prevId": "62f0c43f-599e-4093-8bc3-5237797cf65b", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "account_userId_idx": { + "name": "account_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "account_issuer_subject_uidx": { + "name": "account_issuer_subject_uidx", + "columns": [ + { + "expression": "issuer", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.jwks": { + "name": "jwks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "alg": { + "name": "alg", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "crv": { + "name": "crv", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_access_token": { + "name": "oauth_access_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "refresh_id": { + "name": "refresh_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "revoked": { + "name": "revoked", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "oauthAccessToken_clientId_idx": { + "name": "oauthAccessToken_clientId_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "oauthAccessToken_sessionId_idx": { + "name": "oauthAccessToken_sessionId_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "oauthAccessToken_userId_idx": { + "name": "oauthAccessToken_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "oauthAccessToken_authorizationCodeId_idx": { + "name": "oauthAccessToken_authorizationCodeId_idx", + "columns": [ + { + "expression": "authorization_code_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "oauthAccessToken_refreshId_idx": { + "name": "oauthAccessToken_refreshId_idx", + "columns": [ + { + "expression": "refresh_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "oauth_access_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_access_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_access_token", + "columnsFrom": ["client_id"], + "tableTo": "oauth_client", + "columnsTo": ["client_id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_access_token_session_id_session_id_fk": { + "name": "oauth_access_token_session_id_session_id_fk", + "tableFrom": "oauth_access_token", + "columnsFrom": ["session_id"], + "tableTo": "session", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "oauth_access_token_user_id_user_id_fk": { + "name": "oauth_access_token_user_id_user_id_fk", + "tableFrom": "oauth_access_token", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_access_token_refresh_id_oauth_refresh_token_id_fk": { + "name": "oauth_access_token_refresh_id_oauth_refresh_token_id_fk", + "tableFrom": "oauth_access_token", + "columnsFrom": ["refresh_id"], + "tableTo": "oauth_refresh_token", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_access_token_token_unique": { + "name": "oauth_access_token_token_unique", + "columns": ["token"], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client": { + "name": "oauth_client", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_discovery_id": { + "name": "client_discovery_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "skip_consent": { + "name": "skip_consent", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "enable_end_session": { + "name": "enable_end_session", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "client_credentials_scopes": { + "name": "client_credentials_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon_dark": { + "name": "icon_dark", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contacts": { + "name": "contacts", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "tos": { + "name": "tos", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "policy": { + "name": "policy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_id": { + "name": "software_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_version": { + "name": "software_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_statement": { + "name": "software_statement", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "post_logout_redirect_uris": { + "name": "post_logout_redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_uri": { + "name": "backchannel_logout_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_session_required": { + "name": "backchannel_logout_session_required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "token_endpoint_auth_method": { + "name": "token_endpoint_auth_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_type": { + "name": "application_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks": { + "name": "jwks", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks_uri": { + "name": "jwks_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grant_types": { + "name": "grant_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "response_types": { + "name": "response_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "require_pkce": { + "name": "require_pkce", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens": { + "name": "dpop_bound_access_tokens", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "oauthClient_userId_idx": { + "name": "oauthClient_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "oauth_client_user_id_user_id_fk": { + "name": "oauth_client_user_id_user_id_fk", + "tableFrom": "oauth_client", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_client_client_id_unique": { + "name": "oauth_client_client_id_unique", + "columns": ["client_id"], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client_assertion": { + "name": "oauth_client_assertion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client_resource": { + "name": "oauth_client_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "oauthClientResource_clientId_resourceId_uidx": { + "name": "oauthClientResource_clientId_resourceId_uidx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "oauthClientResource_clientId_idx": { + "name": "oauthClientResource_clientId_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "oauthClientResource_resourceId_idx": { + "name": "oauthClientResource_resourceId_idx", + "columns": [ + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "oauth_client_resource_client_id_oauth_client_client_id_fk": { + "name": "oauth_client_resource_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_client_resource", + "columnsFrom": ["client_id"], + "tableTo": "oauth_client", + "columnsTo": ["client_id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_client_resource_resource_id_oauth_resource_identifier_fk": { + "name": "oauth_client_resource_resource_id_oauth_resource_identifier_fk", + "tableFrom": "oauth_client_resource", + "columnsFrom": ["resource_id"], + "tableTo": "oauth_resource", + "columnsTo": ["identifier"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_consent": { + "name": "oauth_consent", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "oauthConsent_clientId_idx": { + "name": "oauthConsent_clientId_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "oauthConsent_userId_idx": { + "name": "oauthConsent_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "oauth_consent_client_id_oauth_client_client_id_fk": { + "name": "oauth_consent_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_consent", + "columnsFrom": ["client_id"], + "tableTo": "oauth_client", + "columnsTo": ["client_id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_consent_user_id_user_id_fk": { + "name": "oauth_consent_user_id_user_id_fk", + "tableFrom": "oauth_consent", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_refresh_token": { + "name": "oauth_refresh_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "revoked": { + "name": "revoked", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "rotated_at": { + "name": "rotated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_response": { + "name": "rotation_replay_response", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_expires_at": { + "name": "rotation_replay_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "auth_time": { + "name": "auth_time", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "oauthRefreshToken_clientId_idx": { + "name": "oauthRefreshToken_clientId_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "oauthRefreshToken_sessionId_idx": { + "name": "oauthRefreshToken_sessionId_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "oauthRefreshToken_userId_idx": { + "name": "oauthRefreshToken_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "oauthRefreshToken_authorizationCodeId_idx": { + "name": "oauthRefreshToken_authorizationCodeId_idx", + "columns": [ + { + "expression": "authorization_code_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "oauth_refresh_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_refresh_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_refresh_token", + "columnsFrom": ["client_id"], + "tableTo": "oauth_client", + "columnsTo": ["client_id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_refresh_token_session_id_session_id_fk": { + "name": "oauth_refresh_token_session_id_session_id_fk", + "tableFrom": "oauth_refresh_token", + "columnsFrom": ["session_id"], + "tableTo": "session", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "oauth_refresh_token_user_id_user_id_fk": { + "name": "oauth_refresh_token_user_id_user_id_fk", + "tableFrom": "oauth_refresh_token", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_refresh_token_token_unique": { + "name": "oauth_refresh_token_token_unique", + "columns": ["token"], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_resource": { + "name": "oauth_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token_ttl": { + "name": "access_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refresh_token_ttl": { + "name": "refresh_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "signing_algorithm": { + "name": "signing_algorithm", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signing_key_id": { + "name": "signing_key_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowed_scopes": { + "name": "allowed_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "custom_claims": { + "name": "custom_claims", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens_required": { + "name": "dpop_bound_access_tokens_required", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "policy_version": { + "name": "policy_version", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_resource_identifier_unique": { + "name": "oauth_resource_identifier_unique", + "columns": ["identifier"], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.passkey": { + "name": "passkey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "counter": { + "name": "counter", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_type": { + "name": "device_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backed_up": { + "name": "backed_up", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "transports": { + "name": "transports", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "aaguid": { + "name": "aaguid", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "passkey_userId_idx": { + "name": "passkey_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "passkey_user_id_user_id_fk": { + "name": "passkey_user_id_user_id_fk", + "tableFrom": "passkey", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "passkey_credential_id_unique": { + "name": "passkey_credential_id_unique", + "columns": ["credential_id"], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rate_limit": { + "name": "rate_limit", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "last_request": { + "name": "last_request", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "rate_limit_key_unique": { + "name": "rate_limit_key_unique", + "columns": ["key"], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "session_userId_idx": { + "name": "session_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "columns": ["token"], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "columns": ["email"], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.identity_evidence": { + "name": "identity_evidence", + "schema": "", + "columns": { + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "states": { + "name": "states", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "valid_until": { + "name": "valid_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "telegram_id": { + "name": "telegram_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegram_username": { + "name": "telegram_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "identity_evidence_telegram_id_uidx": { + "name": "identity_evidence_telegram_id_uidx", + "columns": [ + { + "expression": "telegram_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"identity_evidence\".\"telegram_id\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "identity_evidence_issuer_subject_pk": { + "name": "identity_evidence_issuer_subject_pk", + "columns": ["issuer", "subject"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.student_verification_challenge": { + "name": "student_verification_challenge", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code_hash": { + "name": "code_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_sent_at": { + "name": "last_sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "studentVerificationChallenge_email_uidx": { + "name": "studentVerificationChallenge_email_uidx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "studentVerificationChallenge_expiresAt_idx": { + "name": "studentVerificationChallenge_expiresAt_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "student_verification_challenge_user_id_user_id_fk": { + "name": "student_verification_challenge_user_id_user_id_fk", + "tableFrom": "student_verification_challenge", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permission": { + "name": "permission", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "managed": { + "name": "managed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "permission_key_uidx": { + "name": "permission_key_uidx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permission_implication": { + "name": "permission_implication", + "schema": "", + "columns": { + "permission_id": { + "name": "permission_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "implied_permission_id": { + "name": "implied_permission_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "permissionImplication_implied_idx": { + "name": "permissionImplication_implied_idx", + "columns": [ + { + "expression": "implied_permission_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "permission_implication_permission_id_permission_id_fk": { + "name": "permission_implication_permission_id_permission_id_fk", + "tableFrom": "permission_implication", + "columnsFrom": ["permission_id"], + "tableTo": "permission", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "permission_implication_implied_permission_id_permission_id_fk": { + "name": "permission_implication_implied_permission_id_permission_id_fk", + "tableFrom": "permission_implication", + "columnsFrom": ["implied_permission_id"], + "tableTo": "permission", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "permission_implication_permission_id_implied_permission_id_pk": { + "name": "permission_implication_permission_id_implied_permission_id_pk", + "columns": ["permission_id", "implied_permission_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rbac_audit_event": { + "name": "rbac_audit_event", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operation": { + "name": "operation", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "before": { + "name": "before", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "after": { + "name": "after", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.role": { + "name": "role", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "managed": { + "name": "managed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "source_state": { + "name": "source_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "role_key_uidx": { + "name": "role_key_uidx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.role_parent": { + "name": "role_parent", + "schema": "", + "columns": { + "role_id": { + "name": "role_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_role_id": { + "name": "parent_role_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "roleParent_parent_idx": { + "name": "roleParent_parent_idx", + "columns": [ + { + "expression": "parent_role_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "role_parent_role_id_role_id_fk": { + "name": "role_parent_role_id_role_id_fk", + "tableFrom": "role_parent", + "columnsFrom": ["role_id"], + "tableTo": "role", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "role_parent_parent_role_id_role_id_fk": { + "name": "role_parent_parent_role_id_role_id_fk", + "tableFrom": "role_parent", + "columnsFrom": ["parent_role_id"], + "tableTo": "role", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "role_parent_role_id_parent_role_id_pk": { + "name": "role_parent_role_id_parent_role_id_pk", + "columns": ["role_id", "parent_role_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.role_permission": { + "name": "role_permission", + "schema": "", + "columns": { + "role_id": { + "name": "role_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_id": { + "name": "permission_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "rolePermission_permission_idx": { + "name": "rolePermission_permission_idx", + "columns": [ + { + "expression": "permission_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "role_permission_role_id_role_id_fk": { + "name": "role_permission_role_id_role_id_fk", + "tableFrom": "role_permission", + "columnsFrom": ["role_id"], + "tableTo": "role", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "role_permission_permission_id_permission_id_fk": { + "name": "role_permission_permission_id_permission_id_fk", + "tableFrom": "role_permission", + "columnsFrom": ["permission_id"], + "tableTo": "permission", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "role_permission_role_id_permission_id_pk": { + "name": "role_permission_role_id_permission_id_pk", + "columns": ["role_id", "permission_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_role": { + "name": "user_role", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assigned_by": { + "name": "assigned_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assignedAt": { + "name": "assignedAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "userRole_role_idx": { + "name": "userRole_role_idx", + "columns": [ + { + "expression": "role_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "user_role_user_id_user_id_fk": { + "name": "user_role_user_id_user_id_fk", + "tableFrom": "user_role", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_role_role_id_role_id_fk": { + "name": "user_role_role_id_role_id_fk", + "tableFrom": "user_role", + "columnsFrom": ["role_id"], + "tableTo": "role", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "user_role_user_id_role_id_pk": { + "name": "user_role_user_id_role_id_pk", + "columns": ["user_id", "role_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.access_outbox": { + "name": "access_outbox", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "name": "access_outbox_id_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "always" + } + }, + "projection": { + "name": "projection", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.access_snapshot_state": { + "name": "access_snapshot_state", + "schema": "", + "columns": { + "projection": { + "name": "projection", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entra_group_observation": { + "name": "entra_group_observation", + "schema": "", + "columns": { + "source": { + "name": "source", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "members": { + "name": "members", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "observed_at": { + "name": "observed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "views": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 748036a..8f9ba08 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -141,6 +141,13 @@ "when": 1791823091127, "tag": "0019_oauth_client_icon_dark", "breakpoints": true + }, + { + "idx": 20, + "version": "7", + "when": 1791892585254, + "tag": "0020_azure_members_write_permission", + "breakpoints": true } ] }