@friggframework/core 2.0.0--canary.680.daae24e.0 → 2.0.0--canary.686.e94de4d.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/CHANGELOG.md +0 -11
- package/CLAUDE.md +28 -0
- package/README.md +105 -0
- package/application/commands/integration-commands.js +2 -2
- package/application/commands/scheduler-commands.js +11 -7
- package/application/commands/user-commands.js +17 -5
- package/core/Worker.js +31 -7
- package/core/create-handler.js +9 -0
- package/credential/use-cases/credential-operations-for-user.js +110 -25
- package/credential/use-cases/reauthorize-credential-for-user.js +15 -8
- package/database/config.js +48 -24
- package/database/documentdb-utils.js +6 -1
- package/database/encryption/README.md +0 -2
- package/database/encryption/encryption-config.js +10 -31
- package/database/encryption/encryption-schema-registry.js +99 -47
- package/database/encryption/field-encryption-service.js +5 -69
- package/database/organization-where.js +18 -0
- package/database/prisma.js +4 -2
- package/database/unique-violation.js +25 -0
- package/extensions/bootstrap-runner.js +43 -0
- package/extensions/extension-loader.js +187 -0
- package/extensions/index.js +41 -0
- package/extensions/initialize-app.js +123 -0
- package/extensions/route-mounter.js +103 -0
- package/extensions/schema-composer.js +188 -0
- package/generated/prisma-mongodb/edge.js +27 -4
- package/generated/prisma-mongodb/index-browser.js +24 -1
- package/generated/prisma-mongodb/index.d.ts +3227 -47
- package/generated/prisma-mongodb/index.js +27 -4
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +65 -0
- package/generated/prisma-mongodb/wasm.js +27 -4
- package/generated/prisma-postgresql/edge.js +27 -4
- package/generated/prisma-postgresql/index-browser.js +24 -1
- package/generated/prisma-postgresql/index.d.ts +3519 -67
- package/generated/prisma-postgresql/index.js +27 -4
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +61 -0
- package/generated/prisma-postgresql/wasm.js +27 -4
- package/handlers/app-definition-loader.js +103 -12
- package/handlers/routers/auth.js +40 -12
- package/handlers/routers/health.js +4 -2
- package/handlers/routers/integration-defined-routers.js +144 -92
- package/handlers/routers/integration-webhook-routers.js +102 -64
- package/index.js +63 -1
- package/infrastructure/scheduler/index.js +19 -6
- package/infrastructure/scheduler/netlify-scheduler-adapter.js +216 -0
- package/infrastructure/scheduler/scheduled-job-repository.js +118 -0
- package/infrastructure/scheduler/scheduler-service-factory.js +55 -7
- package/integrations/index.js +5 -0
- package/integrations/integration-router.js +115 -73
- package/integrations/repositories/integration-not-found.js +14 -0
- package/integrations/repositories/integration-repository-documentdb.js +35 -3
- package/integrations/repositories/integration-repository-interface.js +17 -1
- package/integrations/repositories/integration-repository-mongo.js +59 -76
- package/integrations/repositories/integration-repository-postgres.js +62 -76
- package/integrations/schedule-config.js +80 -0
- package/integrations/tests/doubles/test-integration-repository.js +12 -1
- package/integrations/use-cases/create-integration.js +55 -20
- package/integrations/use-cases/dispatch-ongoing-syncs.js +100 -0
- package/integrations/use-cases/get-integrations-for-user.js +80 -40
- package/integrations/use-cases/index.js +7 -1
- package/integrations/use-cases/integration-operations-for-user.js +55 -24
- package/integrations/use-cases/resolve-integration-ownership.js +177 -0
- package/integrations/utils/map-integration-dto.js +44 -1
- package/management-api/management-api-config.js +71 -2
- package/management-api/meta-router.js +21 -2
- package/management-api/openapi/components.js +40 -0
- package/management-api/openapi/v2-operations.js +16 -4
- package/management-api/route-registry.js +123 -6
- package/management-api/v2/dependencies.js +40 -15
- package/management-api/v2/dto.js +20 -0
- package/management-api/v2/handlers/authorization.js +1 -0
- package/management-api/v2/handlers/credentials.js +6 -2
- package/management-api/v2/handlers/entities.js +10 -7
- package/management-api/v2/handlers/integrations.js +6 -3
- package/management-api/v2/request.js +32 -1
- package/modules/entity-scope.js +15 -0
- package/modules/index.js +8 -0
- package/modules/module-factory.js +12 -0
- package/modules/module.js +53 -2
- package/modules/repositories/entity-not-found.js +18 -0
- package/modules/repositories/module-repository-documentdb.js +51 -6
- package/modules/repositories/module-repository-interface.js +19 -2
- package/modules/repositories/module-repository-mongo.js +67 -74
- package/modules/repositories/module-repository-postgres.js +66 -74
- package/modules/repositories/module-repository.js +2 -1
- package/modules/scope-config.js +192 -0
- package/modules/scope-policy.js +112 -0
- package/modules/scope-resolution.js +288 -0
- package/modules/scope-visibility.js +247 -0
- package/modules/tests/doubles/ownership-matching.js +18 -0
- package/modules/use-cases/authorize-entity-for-user.js +61 -0
- package/modules/use-cases/create-entity-from-credential.js +113 -0
- package/modules/use-cases/entity-operations-for-user.js +50 -13
- package/modules/use-cases/entity-types.js +31 -7
- package/modules/use-cases/execute-entity-proxy-request.js +50 -3
- package/modules/use-cases/get-authorization-requirements.js +2 -2
- package/modules/use-cases/get-entities-for-user.js +26 -13
- package/modules/use-cases/get-entity-module-for-user.js +34 -7
- package/modules/use-cases/get-entity-options-by-id.js +10 -4
- package/modules/use-cases/get-entity-options-by-type.js +1 -1
- package/modules/use-cases/get-module-instance-from-type.js +1 -1
- package/modules/use-cases/get-module.js +10 -4
- package/modules/use-cases/manage-access.js +21 -0
- package/modules/use-cases/multi-step-authorization.js +40 -13
- package/modules/use-cases/process-authorization-callback.js +53 -19
- package/modules/use-cases/refresh-entity-options.js +10 -4
- package/modules/use-cases/test-module-auth.js +10 -4
- package/package.json +6 -5
- package/prisma-mongodb/schema.prisma +65 -0
- package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +26 -0
- package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +25 -0
- package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +28 -0
- package/prisma-postgresql/schema.prisma +61 -0
- package/providers/index.js +23 -0
- package/providers/resolve-provider.js +93 -0
- package/providers/runtime-detection.js +124 -0
- package/queues/index.js +25 -0
- package/queues/providers/index.js +11 -0
- package/queues/providers/netlify-background-provider.js +150 -0
- package/queues/providers/qstash-queue-provider.js +219 -0
- package/queues/providers/sqs-queue-provider.js +70 -0
- package/queues/queue-client-interface.js +55 -0
- package/queues/queue-message-auth.js +193 -0
- package/queues/queue-provider-factory.js +101 -0
- package/queues/queue-provider.js +65 -0
- package/queues/sqs-queue-client.js +81 -0
- package/types/index.d.ts +1 -0
- package/types/modules/index.d.ts +139 -0
- package/types/user/index.d.ts +134 -0
- package/user/adopter-jwt-config.js +177 -0
- package/user/adopter-jwt-errors.js +59 -0
- package/user/adopter-token-verifier.js +122 -0
- package/user/app-user-helpers.js +97 -0
- 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 +106 -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 +27 -2
- 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 +220 -0
- package/user/use-cases/authenticate-user.js +7 -1
- 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 +95 -124
- package/user/use-cases/get-user-from-x-frigg-headers.js +83 -80
- package/user/user.js +64 -1
- package/database/encryption/test-support/fake-ciphertext.js +0 -15
package/user/user.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
const bcrypt = require('bcryptjs');
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* How the request that produced a `User` proved who it is. Set by
|
|
5
|
+
* AuthenticateUser, never inferred from a missing value. Organization roles
|
|
6
|
+
* (ADR-056 §3, §3.6) depend on it: a shared-secret caller is the app's own
|
|
7
|
+
* backend and is trusted to manage; a Frigg bearer token keeps its pre-roles
|
|
8
|
+
* behaviour unless the app opts in (`user.orgAdminGateBearerTokens`); an
|
|
9
|
+
* adopter token has the role it carries, else the stored membership role,
|
|
10
|
+
* else `MEMBER`.
|
|
11
|
+
*/
|
|
12
|
+
const AUTH_SOURCES = Object.freeze({
|
|
13
|
+
SHARED_SECRET: 'shared-secret',
|
|
14
|
+
ADOPTER_JWT: 'adopter-jwt',
|
|
15
|
+
BEARER_TOKEN: 'bearer-token',
|
|
16
|
+
});
|
|
17
|
+
|
|
3
18
|
/**
|
|
4
19
|
* Represents a user in the system. The User class is a domain entity,
|
|
5
20
|
* @class User
|
|
@@ -18,6 +33,14 @@ class User {
|
|
|
18
33
|
this.individualUser = individualUser;
|
|
19
34
|
this.organizationUser = organizationUser;
|
|
20
35
|
this.usePassword = usePassword;
|
|
36
|
+
// Organization role from an adopter token claim (ADR-056 §2), per
|
|
37
|
+
// request. When present it wins over the stored membership role.
|
|
38
|
+
this.orgRole = null;
|
|
39
|
+
// The role stored on the person's membership of the active organization
|
|
40
|
+
// (OrganizationMembership.orgRole); used when the token carries none.
|
|
41
|
+
this.membershipRole = null;
|
|
42
|
+
// How this request authenticated (one of AUTH_SOURCES); null until AuthenticateUser says.
|
|
43
|
+
this.authSource = null;
|
|
21
44
|
|
|
22
45
|
this.config = {
|
|
23
46
|
primary,
|
|
@@ -26,6 +49,17 @@ class User {
|
|
|
26
49
|
};
|
|
27
50
|
}
|
|
28
51
|
|
|
52
|
+
/**
|
|
53
|
+
* The organization this request acts in (ADR-056 §3.6): the one the token or
|
|
54
|
+
* `x-frigg-appOrgId` named, not the person's legacy `organizationId`
|
|
55
|
+
* pointer. Null when the request has no organization.
|
|
56
|
+
* @returns {string|null}
|
|
57
|
+
*/
|
|
58
|
+
getActiveOrganizationId() {
|
|
59
|
+
const id = this.organizationUser?.id;
|
|
60
|
+
return id === undefined || id === null || id === '' ? null : String(id);
|
|
61
|
+
}
|
|
62
|
+
|
|
29
63
|
getPrimaryUser() {
|
|
30
64
|
if (this.config.primary === 'organization') {
|
|
31
65
|
return this.organizationUser;
|
|
@@ -33,6 +67,11 @@ class User {
|
|
|
33
67
|
return this.individualUser;
|
|
34
68
|
}
|
|
35
69
|
|
|
70
|
+
/**
|
|
71
|
+
* The id of the primary user. With `primary: 'organization'` this is the
|
|
72
|
+
* active organization (the one the request named), never the organization
|
|
73
|
+
* the person was first linked to.
|
|
74
|
+
*/
|
|
36
75
|
getId() {
|
|
37
76
|
return this.getPrimaryUser()?.id;
|
|
38
77
|
}
|
|
@@ -65,6 +104,30 @@ class User {
|
|
|
65
104
|
return this.config.individualUserRequired;
|
|
66
105
|
}
|
|
67
106
|
|
|
107
|
+
getAuthSource() {
|
|
108
|
+
return this.authSource;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
setAuthSource(authSource) {
|
|
112
|
+
this.authSource = authSource ?? null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getOrgRole() {
|
|
116
|
+
return this.orgRole;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
setOrgRole(orgRole) {
|
|
120
|
+
this.orgRole = orgRole ?? null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
getMembershipRole() {
|
|
124
|
+
return this.membershipRole;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
setMembershipRole(membershipRole) {
|
|
128
|
+
this.membershipRole = membershipRole ?? null;
|
|
129
|
+
}
|
|
130
|
+
|
|
68
131
|
getIndividualUser() {
|
|
69
132
|
return this.individualUser;
|
|
70
133
|
}
|
|
@@ -122,4 +185,4 @@ class User {
|
|
|
122
185
|
}
|
|
123
186
|
}
|
|
124
187
|
|
|
125
|
-
module.exports = { User };
|
|
188
|
+
module.exports = { User, AUTH_SOURCES };
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test double for Cryptor output. Same shape as the real thing
|
|
3
|
-
* (`keyId:iv:ciphertext:encryptedKey`: base64, 16-byte hex, hex, base64) so
|
|
4
|
-
* FieldEncryptionService._isEncrypted accepts it, with the plaintext
|
|
5
|
-
* recoverable for the fake decrypt.
|
|
6
|
-
*/
|
|
7
|
-
const fakeCiphertext = (plain) =>
|
|
8
|
-
`a2V5SWQ=:${'0'.repeat(32)}:${Buffer.from(String(plain)).toString(
|
|
9
|
-
'hex'
|
|
10
|
-
)}:ZW5jS2V5`;
|
|
11
|
-
|
|
12
|
-
const fakePlaintext = (ciphertext) =>
|
|
13
|
-
Buffer.from(String(ciphertext).split(':')[2], 'hex').toString();
|
|
14
|
-
|
|
15
|
-
module.exports = { fakeCiphertext, fakePlaintext };
|