@rebasepro/common 0.21.1 → 0.21.2-canary.g1ea48be

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.
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Which columns of the user store are the server's alone, and which properties
3
+ * of a given auth collection hold one without saying so.
4
+ *
5
+ * `users` is the one collection a project is expected to redeclare: it is
6
+ * scaffolded into `config/collections/users.ts` so the panel can present it, and
7
+ * the declaration replaces the default outright rather than merging with it. The
8
+ * flag that goes missing unnoticed in that copy is `excludeFromApi` — the
9
+ * neighbouring `admin.hideFromCollection` and `admin.disabled.hidden` keep the
10
+ * field off the screen and look like they did the job.
11
+ *
12
+ * The rule has two readers and they must agree. The server restores the flag so
13
+ * a read does not serve the scrypt hash and a write cannot set it. The panel
14
+ * needs it just as much: a form opens with a value for every property that is
15
+ * not excluded, and submits that baseline, so a panel that does not know sends
16
+ * `passwordHash: null` with every new user and the server refuses the create.
17
+ */
18
+ /**
19
+ * What the rule reads off a collection. A `CollectionConfig` is one; so is a
20
+ * collection a server has loaded and not yet validated.
21
+ */
22
+ export interface AuthSecretCandidate {
23
+ auth?: unknown;
24
+ properties?: Record<string, {
25
+ excludeFromApi?: boolean;
26
+ columnName?: string;
27
+ } | undefined>;
28
+ }
29
+ /**
30
+ * The keys of the properties on `collection` that hold an auth secret and do not
31
+ * carry `excludeFromApi`. Empty for any collection that is not the user store: a
32
+ * `password_hash` column elsewhere — a CRM importing hashes, say — is that
33
+ * project's data, not this rule's business.
34
+ */
35
+ export declare function authSecretsMissingExclusion(collection: AuthSecretCandidate): string[];
@@ -1,3 +1,4 @@
1
+ export * from "./auth-secrets.js";
1
2
  export * from "./CollectionRegistry.js";
2
3
  export * from "./default-collections.js";
3
4
  export * from "./field-access.js";
package/dist/index.es.js CHANGED
@@ -2816,7 +2816,7 @@ var DEFAULT_GUARDED_OPS = [
2816
2816
  "delete"
2817
2817
  ];
2818
2818
  /** Whether a collection is flagged as an authentication collection. */
2819
- function isAuthCollection(collection) {
2819
+ function isAuthCollection$1(collection) {
2820
2820
  const auth = collection.auth;
2821
2821
  return auth === true || typeof auth === "object" && auth?.enabled === true;
2822
2822
  }
@@ -2869,7 +2869,7 @@ function getEffectiveSecurityRules(collection) {
2869
2869
  if (isPostgresCollectionConfig(collection) && collection.disableDefaultPolicies) return [
2870
2870
  ...explicit,
2871
2871
  ...tenantRule(collection),
2872
- ...isAuthCollection(collection) ? [adminWriteGate(tableName)] : []
2872
+ ...isAuthCollection$1(collection) ? [adminWriteGate(tableName)] : []
2873
2873
  ];
2874
2874
  injected.push({
2875
2875
  name: `${tableName}_default_admin_read`,
@@ -2882,7 +2882,7 @@ function getEffectiveSecurityRules(collection) {
2882
2882
  condition: SERVER_OR_ADMIN_EXPR$1,
2883
2883
  check: SERVER_OR_ADMIN_EXPR$1
2884
2884
  });
2885
- if (isAuthCollection(collection)) {
2885
+ if (isAuthCollection$1(collection)) {
2886
2886
  injected.push({
2887
2887
  name: `${tableName}_default_self_read`,
2888
2888
  operations: ["select"],
@@ -2905,7 +2905,7 @@ function getEffectiveSecurityRules(collection) {
2905
2905
  * DDL, which policies are injected and how to take them off.
2906
2906
  */
2907
2907
  function getInjectedSecurityRules(collection) {
2908
- if (isPostgresCollectionConfig(collection) && collection.disableDefaultPolicies) return [...tenantRule(collection), ...isAuthCollection(collection) ? [adminWriteGate(getTableName(collection))] : []];
2908
+ if (isPostgresCollectionConfig(collection) && collection.disableDefaultPolicies) return [...tenantRule(collection), ...isAuthCollection$1(collection) ? [adminWriteGate(getTableName(collection))] : []];
2909
2909
  const explicitCount = (collection.securityRules ?? []).length;
2910
2910
  return getEffectiveSecurityRules(collection).slice(explicitCount);
2911
2911
  }
@@ -3780,6 +3780,165 @@ function firstSqlRow(result) {
3780
3780
  return sqlRows(result)[0];
3781
3781
  }
3782
3782
  //#endregion
3783
+ //#region src/collections/default-collections.ts
3784
+ /**
3785
+ * Default users collection.
3786
+ *
3787
+ * Prepended to the developer's collections array by the admin and server.
3788
+ * Slug-based dedup (Map keyed by slug, last-write-wins) lets developers
3789
+ * override by defining their own collection with `slug: "users"`.
3790
+ *
3791
+ * Schema only — no `admin` block. This package is on the backend's dependency path,
3792
+ * where that field does not exist: `@rebasepro/cms-types` adds it by declaration
3793
+ * merging, and a BaaS install never installs that. The scaffolded
3794
+ * `config/collections/users.ts` carries the presentation for projects that want this
3795
+ * collection in their panel, which is also where it is editable.
3796
+ */
3797
+ var defaultUsersCollection = defineCollection({
3798
+ name: "Users",
3799
+ singularName: "User",
3800
+ slug: "users",
3801
+ auth: true,
3802
+ table: "users",
3803
+ schema: "rebase",
3804
+ securityRules: [{
3805
+ operation: "select",
3806
+ roles: ["admin"]
3807
+ }, {
3808
+ operations: [
3809
+ "insert",
3810
+ "update",
3811
+ "delete"
3812
+ ],
3813
+ roles: ["admin"]
3814
+ }],
3815
+ properties: {
3816
+ id: {
3817
+ name: "ID",
3818
+ type: "string",
3819
+ isId: "uuid"
3820
+ },
3821
+ email: {
3822
+ name: "Email",
3823
+ type: "string",
3824
+ validation: {
3825
+ required: true,
3826
+ unique: true
3827
+ }
3828
+ },
3829
+ displayName: {
3830
+ name: "Name",
3831
+ type: "string",
3832
+ columnName: "display_name",
3833
+ validation: { required: true }
3834
+ },
3835
+ photoURL: {
3836
+ name: "Photo URL",
3837
+ type: "string",
3838
+ columnName: "photo_url"
3839
+ },
3840
+ roles: {
3841
+ name: "Roles",
3842
+ type: "array",
3843
+ columnType: "text[]",
3844
+ of: {
3845
+ name: "Role",
3846
+ type: "string",
3847
+ enum: {
3848
+ admin: "Admin",
3849
+ editor: "Editor",
3850
+ viewer: "Viewer"
3851
+ }
3852
+ }
3853
+ },
3854
+ passwordHash: {
3855
+ name: "Password Hash",
3856
+ type: "string",
3857
+ columnName: "password_hash",
3858
+ excludeFromApi: true
3859
+ },
3860
+ emailVerified: {
3861
+ name: "Email Verified",
3862
+ type: "boolean",
3863
+ columnName: "email_verified",
3864
+ defaultValue: false
3865
+ },
3866
+ emailVerificationToken: {
3867
+ name: "Email Verification Token",
3868
+ type: "string",
3869
+ columnName: "email_verification_token",
3870
+ excludeFromApi: true
3871
+ },
3872
+ emailVerificationSentAt: {
3873
+ name: "Email Verification Sent At",
3874
+ type: "date",
3875
+ columnName: "email_verification_sent_at"
3876
+ },
3877
+ metadata: {
3878
+ name: "Metadata",
3879
+ type: "map",
3880
+ keyValue: true,
3881
+ properties: {},
3882
+ defaultValue: {}
3883
+ },
3884
+ createdAt: {
3885
+ name: "Created At",
3886
+ type: "date",
3887
+ columnName: "created_at",
3888
+ autoValue: "on_create"
3889
+ },
3890
+ updatedAt: {
3891
+ name: "Updated At",
3892
+ type: "date",
3893
+ columnName: "updated_at",
3894
+ autoValue: "on_update"
3895
+ }
3896
+ }
3897
+ });
3898
+ //#endregion
3899
+ //#region src/collections/auth-secrets.ts
3900
+ /** Is this collection the one the auth subsystem stores users in? */
3901
+ function isAuthCollection(collection) {
3902
+ const auth = collection.auth;
3903
+ if (auth === true) return true;
3904
+ return typeof auth === "object" && auth !== null && "enabled" in auth && auth.enabled === true;
3905
+ }
3906
+ /**
3907
+ * The column names, and property keys, the default users collection marks
3908
+ * `excludeFromApi`.
3909
+ *
3910
+ * Both spellings, because a redeclaration is free to rename the property: one
3911
+ * project's copy says `password_hash` where the default says `passwordHash`, and
3912
+ * the column is the same either way. Read off {@link defaultUsersCollection}, so
3913
+ * a secret added there is covered on the same commit.
3914
+ */
3915
+ function defaultSecretNames() {
3916
+ const properties = defaultUsersCollection.properties;
3917
+ const names = /* @__PURE__ */ new Set();
3918
+ for (const [key, property] of Object.entries(properties)) {
3919
+ if (!property?.excludeFromApi) continue;
3920
+ names.add(key);
3921
+ if (property.columnName) names.add(property.columnName);
3922
+ }
3923
+ return names;
3924
+ }
3925
+ /**
3926
+ * The keys of the properties on `collection` that hold an auth secret and do not
3927
+ * carry `excludeFromApi`. Empty for any collection that is not the user store: a
3928
+ * `password_hash` column elsewhere — a CRM importing hashes, say — is that
3929
+ * project's data, not this rule's business.
3930
+ */
3931
+ function authSecretsMissingExclusion(collection) {
3932
+ if (!collection.properties || !isAuthCollection(collection)) return [];
3933
+ const secrets = defaultSecretNames();
3934
+ const missing = [];
3935
+ for (const [key, property] of Object.entries(collection.properties)) {
3936
+ if (!property || property.excludeFromApi) continue;
3937
+ if (secrets.has(key) || secrets.has(property.columnName ?? key)) missing.push(key);
3938
+ }
3939
+ return missing;
3940
+ }
3941
+ //#endregion
3783
3942
  //#region src/data/resolveDataSource.ts
3784
3943
  /**
3785
3944
  * Build a keyed registry from a list of {@link DataSourceDefinition}s.
@@ -3986,7 +4145,9 @@ var CollectionRegistry = class {
3986
4145
  if (!result.dataSource) result.dataSource = resolved.key;
3987
4146
  if (!result.engine) result.engine = resolved.engine;
3988
4147
  }
3989
- result.properties = this.normalizeProperties(result.properties, result);
4148
+ const properties = this.normalizeProperties(result.properties, result);
4149
+ result.properties = properties;
4150
+ for (const key of authSecretsMissingExclusion(result)) properties[key].excludeFromApi = true;
3990
4151
  return result;
3991
4152
  }
3992
4153
  normalizeProperties(properties, collection) {
@@ -4104,122 +4265,6 @@ var CollectionRegistry = class {
4104
4265
  }
4105
4266
  };
4106
4267
  //#endregion
4107
- //#region src/collections/default-collections.ts
4108
- /**
4109
- * Default users collection.
4110
- *
4111
- * Prepended to the developer's collections array by the admin and server.
4112
- * Slug-based dedup (Map keyed by slug, last-write-wins) lets developers
4113
- * override by defining their own collection with `slug: "users"`.
4114
- *
4115
- * Schema only — no `admin` block. This package is on the backend's dependency path,
4116
- * where that field does not exist: `@rebasepro/cms-types` adds it by declaration
4117
- * merging, and a BaaS install never installs that. The scaffolded
4118
- * `config/collections/users.ts` carries the presentation for projects that want this
4119
- * collection in their panel, which is also where it is editable.
4120
- */
4121
- var defaultUsersCollection = defineCollection({
4122
- name: "Users",
4123
- singularName: "User",
4124
- slug: "users",
4125
- auth: true,
4126
- table: "users",
4127
- schema: "rebase",
4128
- securityRules: [{
4129
- operation: "select",
4130
- roles: ["admin"]
4131
- }, {
4132
- operations: [
4133
- "insert",
4134
- "update",
4135
- "delete"
4136
- ],
4137
- roles: ["admin"]
4138
- }],
4139
- properties: {
4140
- id: {
4141
- name: "ID",
4142
- type: "string",
4143
- isId: "uuid"
4144
- },
4145
- email: {
4146
- name: "Email",
4147
- type: "string",
4148
- validation: {
4149
- required: true,
4150
- unique: true
4151
- }
4152
- },
4153
- displayName: {
4154
- name: "Name",
4155
- type: "string",
4156
- columnName: "display_name",
4157
- validation: { required: true }
4158
- },
4159
- photoURL: {
4160
- name: "Photo URL",
4161
- type: "string",
4162
- columnName: "photo_url"
4163
- },
4164
- roles: {
4165
- name: "Roles",
4166
- type: "array",
4167
- columnType: "text[]",
4168
- of: {
4169
- name: "Role",
4170
- type: "string",
4171
- enum: {
4172
- admin: "Admin",
4173
- editor: "Editor",
4174
- viewer: "Viewer"
4175
- }
4176
- }
4177
- },
4178
- passwordHash: {
4179
- name: "Password Hash",
4180
- type: "string",
4181
- columnName: "password_hash",
4182
- excludeFromApi: true
4183
- },
4184
- emailVerified: {
4185
- name: "Email Verified",
4186
- type: "boolean",
4187
- columnName: "email_verified",
4188
- defaultValue: false
4189
- },
4190
- emailVerificationToken: {
4191
- name: "Email Verification Token",
4192
- type: "string",
4193
- columnName: "email_verification_token",
4194
- excludeFromApi: true
4195
- },
4196
- emailVerificationSentAt: {
4197
- name: "Email Verification Sent At",
4198
- type: "date",
4199
- columnName: "email_verification_sent_at"
4200
- },
4201
- metadata: {
4202
- name: "Metadata",
4203
- type: "map",
4204
- keyValue: true,
4205
- properties: {},
4206
- defaultValue: {}
4207
- },
4208
- createdAt: {
4209
- name: "Created At",
4210
- type: "date",
4211
- columnName: "created_at",
4212
- autoValue: "on_create"
4213
- },
4214
- updatedAt: {
4215
- name: "Updated At",
4216
- type: "date",
4217
- columnName: "updated_at",
4218
- autoValue: "on_update"
4219
- }
4220
- }
4221
- });
4222
- //#endregion
4223
4268
  //#region src/collections/field-access.ts
4224
4269
  /**
4225
4270
  * The role that satisfies any non-empty list.
@@ -6950,6 +6995,6 @@ async function detectJunctionTables(executeSql) {
6950
6995
  return junctionTables;
6951
6996
  }
6952
6997
  //#endregion
6953
- export { ADMIN_ROLE, CALLBACK_REJECTED, COLLECTION_PATH_SEPARATOR, COMPOSITE_ID_SEPARATOR, CollectionRegistry, CursorError, CursorMismatchError, DEFAULT_FIND_ALL_MAX_ROWS, DEFAULT_MAX_PAGES, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, DEFAULT_PAGE_SIZE, DEFAULT_STRING_COLUMN_LENGTH, IncludeSpecError, JUNCTION_TABLES_SQL, MAX_LOGICAL_NESTING_DEPTH, OrderBySpecError, QueryBuilder, REBASE_INTERNAL_PREFIXES, REBASE_INTERNAL_SCHEMAS, REBASE_INTERNAL_TABLES, REBASE_USER_ROLE, RebasePaginationError, TENANT_INDEX_REASON, UnknownFilterOperatorError, aggregateAlias, and, applyDefaultValuesOnCreate, buildCollectionFromTableMetadata, buildCompositeId, buildConditionContext, buildPropertyCallbacks, buildRebaseData, buildRoutedRebaseData, buildSdkData, buildTenantSecurityRule, callbackRefusal, canCreateEntity, canDeleteEntity, canEditEntity, canReadCollection, canReadField, canWriteField, checkOperation, classifyTable, collectAllPages, cond, createDataSourceRegistry, createPaginationHelpers, createRelationRef, createRelationRefWithData, cursorToStartAfter, decodeCursor, defaultUsersCollection, defineCollection, denormalizeInclude, deserializeFilter, deserializeInclude, deserializeLogicalCondition, deserializeOrderBy, deserializeOrderByList, detectJunctionTables, effectiveAccess, embedParentExpression, encodeCursor, enumToObjectEntries, evaluateCondition, evaluatePolicy, fieldKeyForColumn, findAnonymousGrants, findRelation, firstSqlRow, fullPathToCollectionSegments, getArrayResolvedProperties, getChildViewDeclaringProperties, getChildViewRelationPropertyKeys, getColumnName, getDeclaredPrimaryKeys, getDefaultValueFor, getDefaultValueFortype, getDefaultValuesFor, getEffectiveSecurityRules, getEntityChildViews, getEnumVarName, getGeneratedPolicyNames, getInjectedSecurityRules, getJunctionCollectionConfig, getJunctionConfigForRelation, getJunctionSecurityRules, getLabelOrConfigFrom, getPrimaryKeys, getReferenceFrom, getRelationFrom, getRelationTargetPath, getSubcollections, getTableName, getTableVarName, getTenantConfig, hasFieldAccessRules, includePaths, isAddressableId, isJunctionBackedRelation, isPropertyBuilder, isRebaseInternalTable, isRelationRequired, isRelationalCollection, mergeIncludeSpecs, normalizeDriverOrderBy, normalizeEmail, normalizeInclude, normalizeOrderBy, normalizeToEntityRelation, not, or, paginateFind, parseIdValues, parseOrderBySpecStrict, policyToPostgres, primaryOrderBy, reconcileCursorOrder, registerConditionOperations, relationDeclaringProperty, relationalCollections, requireCallbackClient, requireCallbackCollection, resolveArrayProperties, resolveCollectionRelations, resolveDataSource, resolveEnumValues, resolveFindWindow, resolveJunctionSpecs, resolvePrimaryKeys, resolveProperties, resolveProperty, resolvePropertyEnum, resolveRelation, resolveRelationProperty, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource, resolveStringColumnLength, resolveTenantWrite, restrictedFieldNames, revokeInternalTableAccess, revokeInternalTableSql, sanitizeData, securityRuleToConditions, segmentsToStrippedPath, serializeFilter, serializeInclude, serializeLogicalCondition, serializeOrderBy, sortCollectionsBySlug, sortProperties, sqlRows, sqlToPolicy, stripCollectionPath, tenantBypassRoles, tenantPolicyName, tenantScopeExpression, toCallbackError, toFilterTuples, topLevelIncludeNames, traverseValueProperty, traverseValuesProperties, updateDateAutoValues, updateUserAutoValues, wrapAsEntityData, wrapAsSdkData };
6998
+ export { ADMIN_ROLE, CALLBACK_REJECTED, COLLECTION_PATH_SEPARATOR, COMPOSITE_ID_SEPARATOR, CollectionRegistry, CursorError, CursorMismatchError, DEFAULT_FIND_ALL_MAX_ROWS, DEFAULT_MAX_PAGES, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, DEFAULT_PAGE_SIZE, DEFAULT_STRING_COLUMN_LENGTH, IncludeSpecError, JUNCTION_TABLES_SQL, MAX_LOGICAL_NESTING_DEPTH, OrderBySpecError, QueryBuilder, REBASE_INTERNAL_PREFIXES, REBASE_INTERNAL_SCHEMAS, REBASE_INTERNAL_TABLES, REBASE_USER_ROLE, RebasePaginationError, TENANT_INDEX_REASON, UnknownFilterOperatorError, aggregateAlias, and, applyDefaultValuesOnCreate, authSecretsMissingExclusion, buildCollectionFromTableMetadata, buildCompositeId, buildConditionContext, buildPropertyCallbacks, buildRebaseData, buildRoutedRebaseData, buildSdkData, buildTenantSecurityRule, callbackRefusal, canCreateEntity, canDeleteEntity, canEditEntity, canReadCollection, canReadField, canWriteField, checkOperation, classifyTable, collectAllPages, cond, createDataSourceRegistry, createPaginationHelpers, createRelationRef, createRelationRefWithData, cursorToStartAfter, decodeCursor, defaultUsersCollection, defineCollection, denormalizeInclude, deserializeFilter, deserializeInclude, deserializeLogicalCondition, deserializeOrderBy, deserializeOrderByList, detectJunctionTables, effectiveAccess, embedParentExpression, encodeCursor, enumToObjectEntries, evaluateCondition, evaluatePolicy, fieldKeyForColumn, findAnonymousGrants, findRelation, firstSqlRow, fullPathToCollectionSegments, getArrayResolvedProperties, getChildViewDeclaringProperties, getChildViewRelationPropertyKeys, getColumnName, getDeclaredPrimaryKeys, getDefaultValueFor, getDefaultValueFortype, getDefaultValuesFor, getEffectiveSecurityRules, getEntityChildViews, getEnumVarName, getGeneratedPolicyNames, getInjectedSecurityRules, getJunctionCollectionConfig, getJunctionConfigForRelation, getJunctionSecurityRules, getLabelOrConfigFrom, getPrimaryKeys, getReferenceFrom, getRelationFrom, getRelationTargetPath, getSubcollections, getTableName, getTableVarName, getTenantConfig, hasFieldAccessRules, includePaths, isAddressableId, isJunctionBackedRelation, isPropertyBuilder, isRebaseInternalTable, isRelationRequired, isRelationalCollection, mergeIncludeSpecs, normalizeDriverOrderBy, normalizeEmail, normalizeInclude, normalizeOrderBy, normalizeToEntityRelation, not, or, paginateFind, parseIdValues, parseOrderBySpecStrict, policyToPostgres, primaryOrderBy, reconcileCursorOrder, registerConditionOperations, relationDeclaringProperty, relationalCollections, requireCallbackClient, requireCallbackCollection, resolveArrayProperties, resolveCollectionRelations, resolveDataSource, resolveEnumValues, resolveFindWindow, resolveJunctionSpecs, resolvePrimaryKeys, resolveProperties, resolveProperty, resolvePropertyEnum, resolveRelation, resolveRelationProperty, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource, resolveStringColumnLength, resolveTenantWrite, restrictedFieldNames, revokeInternalTableAccess, revokeInternalTableSql, sanitizeData, securityRuleToConditions, segmentsToStrippedPath, serializeFilter, serializeInclude, serializeLogicalCondition, serializeOrderBy, sortCollectionsBySlug, sortProperties, sqlRows, sqlToPolicy, stripCollectionPath, tenantBypassRoles, tenantPolicyName, tenantScopeExpression, toCallbackError, toFilterTuples, topLevelIncludeNames, traverseValueProperty, traverseValuesProperties, updateDateAutoValues, updateUserAutoValues, wrapAsEntityData, wrapAsSdkData };
6954
6999
 
6955
7000
  //# sourceMappingURL=index.es.js.map