@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.
Files changed (156) hide show
  1. package/CHANGELOG.md +0 -11
  2. package/CLAUDE.md +28 -0
  3. package/README.md +105 -0
  4. package/application/commands/integration-commands.js +2 -2
  5. package/application/commands/scheduler-commands.js +11 -7
  6. package/application/commands/user-commands.js +17 -5
  7. package/core/Worker.js +31 -7
  8. package/core/create-handler.js +9 -0
  9. package/credential/use-cases/credential-operations-for-user.js +110 -25
  10. package/credential/use-cases/reauthorize-credential-for-user.js +15 -8
  11. package/database/config.js +48 -24
  12. package/database/documentdb-utils.js +6 -1
  13. package/database/encryption/README.md +0 -2
  14. package/database/encryption/encryption-config.js +10 -31
  15. package/database/encryption/encryption-schema-registry.js +99 -47
  16. package/database/encryption/field-encryption-service.js +5 -69
  17. package/database/organization-where.js +18 -0
  18. package/database/prisma.js +4 -2
  19. package/database/unique-violation.js +25 -0
  20. package/extensions/bootstrap-runner.js +43 -0
  21. package/extensions/extension-loader.js +187 -0
  22. package/extensions/index.js +41 -0
  23. package/extensions/initialize-app.js +123 -0
  24. package/extensions/route-mounter.js +103 -0
  25. package/extensions/schema-composer.js +188 -0
  26. package/generated/prisma-mongodb/edge.js +27 -4
  27. package/generated/prisma-mongodb/index-browser.js +24 -1
  28. package/generated/prisma-mongodb/index.d.ts +3227 -47
  29. package/generated/prisma-mongodb/index.js +27 -4
  30. package/generated/prisma-mongodb/package.json +1 -1
  31. package/generated/prisma-mongodb/schema.prisma +65 -0
  32. package/generated/prisma-mongodb/wasm.js +27 -4
  33. package/generated/prisma-postgresql/edge.js +27 -4
  34. package/generated/prisma-postgresql/index-browser.js +24 -1
  35. package/generated/prisma-postgresql/index.d.ts +3519 -67
  36. package/generated/prisma-postgresql/index.js +27 -4
  37. package/generated/prisma-postgresql/package.json +1 -1
  38. package/generated/prisma-postgresql/schema.prisma +61 -0
  39. package/generated/prisma-postgresql/wasm.js +27 -4
  40. package/handlers/app-definition-loader.js +103 -12
  41. package/handlers/routers/auth.js +40 -12
  42. package/handlers/routers/health.js +4 -2
  43. package/handlers/routers/integration-defined-routers.js +144 -92
  44. package/handlers/routers/integration-webhook-routers.js +102 -64
  45. package/index.js +63 -1
  46. package/infrastructure/scheduler/index.js +19 -6
  47. package/infrastructure/scheduler/netlify-scheduler-adapter.js +216 -0
  48. package/infrastructure/scheduler/scheduled-job-repository.js +118 -0
  49. package/infrastructure/scheduler/scheduler-service-factory.js +55 -7
  50. package/integrations/index.js +5 -0
  51. package/integrations/integration-router.js +115 -73
  52. package/integrations/repositories/integration-not-found.js +14 -0
  53. package/integrations/repositories/integration-repository-documentdb.js +35 -3
  54. package/integrations/repositories/integration-repository-interface.js +17 -1
  55. package/integrations/repositories/integration-repository-mongo.js +59 -76
  56. package/integrations/repositories/integration-repository-postgres.js +62 -76
  57. package/integrations/schedule-config.js +80 -0
  58. package/integrations/tests/doubles/test-integration-repository.js +12 -1
  59. package/integrations/use-cases/create-integration.js +55 -20
  60. package/integrations/use-cases/dispatch-ongoing-syncs.js +100 -0
  61. package/integrations/use-cases/get-integrations-for-user.js +80 -40
  62. package/integrations/use-cases/index.js +7 -1
  63. package/integrations/use-cases/integration-operations-for-user.js +55 -24
  64. package/integrations/use-cases/resolve-integration-ownership.js +177 -0
  65. package/integrations/utils/map-integration-dto.js +44 -1
  66. package/management-api/management-api-config.js +71 -2
  67. package/management-api/meta-router.js +21 -2
  68. package/management-api/openapi/components.js +40 -0
  69. package/management-api/openapi/v2-operations.js +16 -4
  70. package/management-api/route-registry.js +123 -6
  71. package/management-api/v2/dependencies.js +40 -15
  72. package/management-api/v2/dto.js +20 -0
  73. package/management-api/v2/handlers/authorization.js +1 -0
  74. package/management-api/v2/handlers/credentials.js +6 -2
  75. package/management-api/v2/handlers/entities.js +10 -7
  76. package/management-api/v2/handlers/integrations.js +6 -3
  77. package/management-api/v2/request.js +32 -1
  78. package/modules/entity-scope.js +15 -0
  79. package/modules/index.js +8 -0
  80. package/modules/module-factory.js +12 -0
  81. package/modules/module.js +53 -2
  82. package/modules/repositories/entity-not-found.js +18 -0
  83. package/modules/repositories/module-repository-documentdb.js +51 -6
  84. package/modules/repositories/module-repository-interface.js +19 -2
  85. package/modules/repositories/module-repository-mongo.js +67 -74
  86. package/modules/repositories/module-repository-postgres.js +66 -74
  87. package/modules/repositories/module-repository.js +2 -1
  88. package/modules/scope-config.js +192 -0
  89. package/modules/scope-policy.js +112 -0
  90. package/modules/scope-resolution.js +288 -0
  91. package/modules/scope-visibility.js +247 -0
  92. package/modules/tests/doubles/ownership-matching.js +18 -0
  93. package/modules/use-cases/authorize-entity-for-user.js +61 -0
  94. package/modules/use-cases/create-entity-from-credential.js +113 -0
  95. package/modules/use-cases/entity-operations-for-user.js +50 -13
  96. package/modules/use-cases/entity-types.js +31 -7
  97. package/modules/use-cases/execute-entity-proxy-request.js +50 -3
  98. package/modules/use-cases/get-authorization-requirements.js +2 -2
  99. package/modules/use-cases/get-entities-for-user.js +26 -13
  100. package/modules/use-cases/get-entity-module-for-user.js +34 -7
  101. package/modules/use-cases/get-entity-options-by-id.js +10 -4
  102. package/modules/use-cases/get-entity-options-by-type.js +1 -1
  103. package/modules/use-cases/get-module-instance-from-type.js +1 -1
  104. package/modules/use-cases/get-module.js +10 -4
  105. package/modules/use-cases/manage-access.js +21 -0
  106. package/modules/use-cases/multi-step-authorization.js +40 -13
  107. package/modules/use-cases/process-authorization-callback.js +53 -19
  108. package/modules/use-cases/refresh-entity-options.js +10 -4
  109. package/modules/use-cases/test-module-auth.js +10 -4
  110. package/package.json +6 -5
  111. package/prisma-mongodb/schema.prisma +65 -0
  112. package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +26 -0
  113. package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +25 -0
  114. package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +28 -0
  115. package/prisma-postgresql/schema.prisma +61 -0
  116. package/providers/index.js +23 -0
  117. package/providers/resolve-provider.js +93 -0
  118. package/providers/runtime-detection.js +124 -0
  119. package/queues/index.js +25 -0
  120. package/queues/providers/index.js +11 -0
  121. package/queues/providers/netlify-background-provider.js +150 -0
  122. package/queues/providers/qstash-queue-provider.js +219 -0
  123. package/queues/providers/sqs-queue-provider.js +70 -0
  124. package/queues/queue-client-interface.js +55 -0
  125. package/queues/queue-message-auth.js +193 -0
  126. package/queues/queue-provider-factory.js +101 -0
  127. package/queues/queue-provider.js +65 -0
  128. package/queues/sqs-queue-client.js +81 -0
  129. package/types/index.d.ts +1 -0
  130. package/types/modules/index.d.ts +139 -0
  131. package/types/user/index.d.ts +134 -0
  132. package/user/adopter-jwt-config.js +177 -0
  133. package/user/adopter-jwt-errors.js +59 -0
  134. package/user/adopter-token-verifier.js +122 -0
  135. package/user/app-user-helpers.js +97 -0
  136. package/user/create-user-authentication.js +64 -0
  137. package/user/membership.js +51 -0
  138. package/user/repositories/organization-membership-record.js +19 -0
  139. package/user/repositories/organization-membership-repository-documentdb.js +106 -0
  140. package/user/repositories/organization-membership-repository-factory.js +42 -0
  141. package/user/repositories/organization-membership-repository-interface.js +57 -0
  142. package/user/repositories/organization-membership-repository-mongo.js +13 -0
  143. package/user/repositories/organization-membership-repository-postgres.js +29 -0
  144. package/user/repositories/organization-membership-repository-prisma.js +81 -0
  145. package/user/repositories/user-repository-documentdb.js +27 -2
  146. package/user/repositories/user-repository-interface.js +18 -0
  147. package/user/repositories/user-repository-mongo.js +24 -0
  148. package/user/repositories/user-repository-postgres.js +18 -0
  149. package/user/tests/doubles/adopter-jwt-fixtures.js +220 -0
  150. package/user/use-cases/authenticate-user.js +7 -1
  151. package/user/use-cases/ensure-organization-membership.js +92 -0
  152. package/user/use-cases/find-individual-users-by-organization.js +54 -0
  153. package/user/use-cases/get-user-from-adopter-jwt.js +95 -124
  154. package/user/use-cases/get-user-from-x-frigg-headers.js +83 -80
  155. package/user/user.js +64 -1
  156. 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 };