@friggframework/core 2.0.0--canary.678.58db4b2.0 → 2.0.0--canary.681.d970b9e.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -3
- package/application/commands/user-commands.js +17 -5
- package/database/documentdb-utils.js +6 -1
- package/database/unique-violation.js +25 -0
- package/generated/prisma-mongodb/edge.js +13 -4
- package/generated/prisma-mongodb/index-browser.js +10 -1
- package/generated/prisma-mongodb/index.d.ts +1922 -94
- package/generated/prisma-mongodb/index.js +13 -4
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +32 -0
- package/generated/prisma-mongodb/wasm.js +13 -4
- package/generated/prisma-postgresql/edge.js +13 -4
- package/generated/prisma-postgresql/index-browser.js +10 -1
- package/generated/prisma-postgresql/index.d.ts +2053 -83
- package/generated/prisma-postgresql/index.js +13 -4
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +29 -0
- package/generated/prisma-postgresql/wasm.js +13 -4
- package/index.js +12 -0
- package/integrations/integration-router.js +8 -30
- package/management-api/v2/dependencies.js +2 -11
- package/package.json +5 -5
- package/prisma-mongodb/schema.prisma +32 -0
- package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +26 -0
- package/prisma-postgresql/schema.prisma +29 -0
- package/types/user/index.d.ts +52 -2
- package/user/adopter-jwt-errors.js +0 -6
- package/user/app-user-helpers.js +32 -8
- package/user/create-user-authentication.js +64 -0
- package/user/membership.js +51 -0
- package/user/repositories/organization-membership-record.js +19 -0
- package/user/repositories/organization-membership-repository-documentdb.js +104 -0
- package/user/repositories/organization-membership-repository-factory.js +42 -0
- package/user/repositories/organization-membership-repository-interface.js +57 -0
- package/user/repositories/organization-membership-repository-mongo.js +13 -0
- package/user/repositories/organization-membership-repository-postgres.js +29 -0
- package/user/repositories/organization-membership-repository-prisma.js +81 -0
- package/user/repositories/user-repository-documentdb.js +23 -0
- package/user/repositories/user-repository-interface.js +18 -0
- package/user/repositories/user-repository-mongo.js +24 -0
- package/user/repositories/user-repository-postgres.js +18 -0
- package/user/tests/doubles/adopter-jwt-fixtures.js +101 -11
- package/user/use-cases/ensure-organization-membership.js +92 -0
- package/user/use-cases/find-individual-users-by-organization.js +54 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +32 -58
- package/user/use-cases/get-user-from-x-frigg-headers.js +49 -38
- package/user/user.js +18 -2
package/README.md
CHANGED
|
@@ -393,7 +393,7 @@ value that is not a three-part JWT is not offered to the verifier at all; it fal
|
|
|
393
393
|
| `clockToleranceSec` | `30` | Skew allowed on `exp` and `nbf`. |
|
|
394
394
|
| `userIdClaim` | `sub` | App user id (required in the token). |
|
|
395
395
|
| `orgIdClaim` | `org_id` | App organization id (optional). |
|
|
396
|
-
| `roleClaim` | `org_role` |
|
|
396
|
+
| `roleClaim` | `org_role` | Exposed as `user.getOrgRole()` for the request, and stored as the `orgRole` of a person's membership the first time they join that organization (`MEMBER` when the token names none). |
|
|
397
397
|
|
|
398
398
|
Rules worth knowing:
|
|
399
399
|
|
|
@@ -406,8 +406,20 @@ Rules worth knowing:
|
|
|
406
406
|
`token_missing_user_claim`, `token_malformed`. The message never contains the token or its claims.
|
|
407
407
|
Any other verification failure, such as an unreachable or unresolvable JWKS, is `503`
|
|
408
408
|
`jwks_unavailable`, not a 401, so clients do not drop good sessions.
|
|
409
|
-
-
|
|
410
|
-
|
|
409
|
+
- **The token's organization is the request's active organization**, and a person can belong to
|
|
410
|
+
several (ADR-056 §3.6). The first time a person presents a token for an organization they are
|
|
411
|
+
not a member of, Frigg adds an `OrganizationMembership` and the request runs as that
|
|
412
|
+
organization (`user.getActiveOrganizationId()`; `user.getId()` under `primary: 'organization'`).
|
|
413
|
+
Nothing is moved: `IndividualUser.organizationId` is a legacy pointer, set only on a person's
|
|
414
|
+
first link and read as an implicit membership. The same applies to `x-frigg-apporgid` with
|
|
415
|
+
`sharedSecret`. With `user.strictMembership: true` those headers cannot add anyone to an organization
|
|
416
|
+
that already existed (400 for a non-member, a brand-new `appUserId` included); only an organization
|
|
417
|
+
the request itself creates is allowed. `strictUserValidation` is a deprecated alias with that same
|
|
418
|
+
meaning. See
|
|
419
|
+
[Organization memberships](../../docs/guides/organization-memberships.md). A token naming a second
|
|
420
|
+
organization is no longer a `403` (`multi_org_unsupported` is gone).
|
|
421
|
+
- Two first logins for the same new person or organization, even in different organizations, both
|
|
422
|
+
succeed: a create that loses to a unique constraint finds the winner's row.
|
|
411
423
|
|
|
412
424
|
### 8. Lambda Utilities (`/lambda`)
|
|
413
425
|
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
const {
|
|
2
2
|
createUserRepository,
|
|
3
3
|
} = require('../../user/repositories/user-repository-factory');
|
|
4
|
+
const {
|
|
5
|
+
createOrganizationMembershipRepository,
|
|
6
|
+
} = require('../../user/repositories/organization-membership-repository-factory');
|
|
7
|
+
const {
|
|
8
|
+
FindIndividualUsersByOrganization,
|
|
9
|
+
} = require('../../user/use-cases/find-individual-users-by-organization');
|
|
4
10
|
|
|
5
11
|
const ERROR_CODE_MAP = {
|
|
6
12
|
USER_NOT_FOUND: 404,
|
|
@@ -26,6 +32,12 @@ function mapErrorToResponse(error) {
|
|
|
26
32
|
*/
|
|
27
33
|
function createUserCommands() {
|
|
28
34
|
const userRepository = createUserRepository();
|
|
35
|
+
const findIndividualUsersByOrganization =
|
|
36
|
+
new FindIndividualUsersByOrganization({
|
|
37
|
+
userRepository,
|
|
38
|
+
organizationMembershipRepository:
|
|
39
|
+
createOrganizationMembershipRepository(),
|
|
40
|
+
});
|
|
29
41
|
|
|
30
42
|
return {
|
|
31
43
|
/**
|
|
@@ -170,7 +182,8 @@ function createUserCommands() {
|
|
|
170
182
|
},
|
|
171
183
|
|
|
172
184
|
/**
|
|
173
|
-
* Find all individual users
|
|
185
|
+
* Find all individual users who belong to an organization user: those with
|
|
186
|
+
* a membership and those the legacy organizationId pointer places there.
|
|
174
187
|
* @param {string} organizationUserId - Organization user ID to search for
|
|
175
188
|
* @returns {Promise<Object[]>} Array of individual user objects (empty if none)
|
|
176
189
|
*/
|
|
@@ -182,10 +195,9 @@ function createUserCommands() {
|
|
|
182
195
|
throw error;
|
|
183
196
|
}
|
|
184
197
|
|
|
185
|
-
const users =
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
);
|
|
198
|
+
const users = await findIndividualUsersByOrganization.execute(
|
|
199
|
+
organizationUserId
|
|
200
|
+
);
|
|
189
201
|
|
|
190
202
|
return (users || []).map((user) => ({
|
|
191
203
|
id: user._id?.toString() || user.id,
|
|
@@ -58,7 +58,12 @@ async function insertOne(client, collection, document) {
|
|
|
58
58
|
|
|
59
59
|
// Provide helpful context for common errors
|
|
60
60
|
if (error.code === 11000) {
|
|
61
|
-
|
|
61
|
+
// `code` lets callers recognise a lost race without parsing text
|
|
62
|
+
// (see database/unique-violation.js).
|
|
63
|
+
throw Object.assign(
|
|
64
|
+
new Error(`${errorMsg} - Duplicate key violation`),
|
|
65
|
+
{ code: 11000 }
|
|
66
|
+
);
|
|
62
67
|
}
|
|
63
68
|
throw new Error(errorMsg);
|
|
64
69
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Mongo/DocumentDB duplicate-key error code. */
|
|
2
|
+
const DUPLICATE_KEY_CODE = 11000;
|
|
3
|
+
/** Prisma's unique-constraint error code (PostgreSQL and MongoDB). */
|
|
4
|
+
const PRISMA_UNIQUE_CODE = 'P2002';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Whether `error` is a unique-constraint violation, however the database
|
|
8
|
+
* layer raised it: a Prisma `P2002`, or a duplicate-key `11000`
|
|
9
|
+
* (`documentdb-utils` `insertOne` sets `code` on the error it throws).
|
|
10
|
+
* Matches on the code only, never on message text.
|
|
11
|
+
*
|
|
12
|
+
* @param {unknown} error
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
15
|
+
function isUniqueViolation(error) {
|
|
16
|
+
return (
|
|
17
|
+
error?.code === PRISMA_UNIQUE_CODE || error?.code === DUPLICATE_KEY_CODE
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
isUniqueViolation,
|
|
23
|
+
DUPLICATE_KEY_CODE,
|
|
24
|
+
PRISMA_UNIQUE_CODE,
|
|
25
|
+
};
|