@memberjunction/core-entities-server 6.1.0-edge.5 → 6.1.0-edge.7
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/dist/custom/MJAIBridgeProviderEntityServer.server.d.ts +1 -5
- package/dist/custom/MJAIBridgeProviderEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJAIBridgeProviderEntityServer.server.js +13 -12
- package/dist/custom/MJAIBridgeProviderEntityServer.server.js.map +1 -1
- package/dist/custom/MJAIRemoteBrowserProviderEntityServer.server.d.ts +2 -6
- package/dist/custom/MJAIRemoteBrowserProviderEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJAIRemoteBrowserProviderEntityServer.server.js +16 -14
- package/dist/custom/MJAIRemoteBrowserProviderEntityServer.server.js.map +1 -1
- package/dist/custom/MJActionEntityServer.server.d.ts +2 -2
- package/dist/custom/MJActionEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJActionEntityServer.server.js +8 -6
- package/dist/custom/MJActionEntityServer.server.js.map +1 -1
- package/dist/custom/MJApplicationEntityServer.server.d.ts +2 -2
- package/dist/custom/MJApplicationEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJApplicationEntityServer.server.js +4 -4
- package/dist/custom/MJApplicationEntityServer.server.js.map +1 -1
- package/dist/custom/MJEntityEntityServer.server.d.ts +36 -0
- package/dist/custom/MJEntityEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJEntityEntityServer.server.js +77 -0
- package/dist/custom/MJEntityEntityServer.server.js.map +1 -0
- package/dist/custom/MJEntityFieldPermissionEntityServer.server.d.ts +167 -0
- package/dist/custom/MJEntityFieldPermissionEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJEntityFieldPermissionEntityServer.server.js +347 -0
- package/dist/custom/MJEntityFieldPermissionEntityServer.server.js.map +1 -0
- package/dist/custom/MJRemoteOperationEntityServer.server.d.ts +2 -2
- package/dist/custom/MJRemoteOperationEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJRemoteOperationEntityServer.server.js +6 -4
- package/dist/custom/MJRemoteOperationEntityServer.server.js.map +1 -1
- package/dist/custom/MJRoleEntityServer.server.d.ts +78 -0
- package/dist/custom/MJRoleEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJRoleEntityServer.server.js +131 -0
- package/dist/custom/MJRoleEntityServer.server.js.map +1 -0
- package/dist/custom/MJUserEntityServer.server.d.ts +243 -0
- package/dist/custom/MJUserEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJUserEntityServer.server.js +335 -0
- package/dist/custom/MJUserEntityServer.server.js.map +1 -0
- package/dist/custom/MJUserRoleEntityServer.server.d.ts +243 -0
- package/dist/custom/MJUserRoleEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJUserRoleEntityServer.server.js +376 -0
- package/dist/custom/MJUserRoleEntityServer.server.js.map +1 -0
- package/dist/custom/MJUserViewEntityServer.server.d.ts +12 -1
- package/dist/custom/MJUserViewEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJUserViewEntityServer.server.js +33 -1
- package/dist/custom/MJUserViewEntityServer.server.js.map +1 -1
- package/dist/custom/fieldPermissionDelta.d.ts +57 -0
- package/dist/custom/fieldPermissionDelta.d.ts.map +1 -0
- package/dist/custom/fieldPermissionDelta.js +196 -0
- package/dist/custom/fieldPermissionDelta.js.map +1 -0
- package/dist/custom/fieldPermissionReconciler.d.ts +34 -0
- package/dist/custom/fieldPermissionReconciler.d.ts.map +1 -0
- package/dist/custom/fieldPermissionReconciler.js +96 -0
- package/dist/custom/fieldPermissionReconciler.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/package.json +28 -28
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { EntityDeleteOptions, EntityFieldInfo, EntitySaveOptions, EntityInfo, FieldPermissionRuleVerbs, ValidationResult } from '@memberjunction/core';
|
|
2
|
+
import { MJEntityFieldPermissionEntity } from '@memberjunction/core-entities';
|
|
3
|
+
/**
|
|
4
|
+
* Server-side `MJ: Entity Field Permissions` entity — the save-time half of the
|
|
5
|
+
* unrestrictable-target guard for field-level security.
|
|
6
|
+
*
|
|
7
|
+
* The runtime aggregation (`EntityFieldInfo.GetUserFieldPermissions`) already forces access
|
|
8
|
+
* open for these targets, so this subclass is not what makes the system safe. It is what makes
|
|
9
|
+
* the system *diagnosable*: without it an administrator can save a row that silently does
|
|
10
|
+
* nothing, then reasonably conclude the feature is broken. Rejecting at save says why.
|
|
11
|
+
*
|
|
12
|
+
* Two categories are refused, matching the two runtime guards exactly:
|
|
13
|
+
*
|
|
14
|
+
* 1. **Primary keys and system columns.** Stripping a primary key from a result breaks entity
|
|
15
|
+
* load, `CompositeKey` construction, relationship resolution, and cache fingerprinting —
|
|
16
|
+
* failures that surface nowhere near the permission record that caused them.
|
|
17
|
+
* 2. **The security-configuration and identity surface** (see
|
|
18
|
+
* {@link EntityFieldInfo.IsOnUnrestrictableEntity}). Restricting a column on the very
|
|
19
|
+
* entities field security is administered through produces a configuration that cannot be
|
|
20
|
+
* reversed through the product — recovery would mean direct SQL against the database.
|
|
21
|
+
*
|
|
22
|
+
* A third category is refused for a different reason: a **`Deny`** aimed at a role the MJ system
|
|
23
|
+
* user holds (see {@link SystemUserRoleRejectionReason}). That one is not about diagnosability —
|
|
24
|
+
* it is what lets the runtime aggregation have no exempt user at all. The server's own account
|
|
25
|
+
* gets its access from ordinary `Allow` rows like everyone else, and this guard is what stops
|
|
26
|
+
* those being revoked.
|
|
27
|
+
*
|
|
28
|
+
* Note what is deliberately NOT here: any notion of a user who is exempt from a Deny at
|
|
29
|
+
* RUNTIME. The guards are on which *targets* are restrictable and on what may be *configured*,
|
|
30
|
+
* never on whose access is evaluated. A feature whose purpose is compensation and donor-giving
|
|
31
|
+
* confidentiality cannot ship with a role that quietly reads everything.
|
|
32
|
+
*/
|
|
33
|
+
export declare class MJEntityFieldPermissionEntityServer extends MJEntityFieldPermissionEntity {
|
|
34
|
+
Validate(): ValidationResult;
|
|
35
|
+
/**
|
|
36
|
+
* Refuses an EDIT that would strip the MJ system user's last `Allow` on a field.
|
|
37
|
+
*
|
|
38
|
+
* Not in `Validate()` for two reasons. It needs the field's rules as they stand in the
|
|
39
|
+
* DATABASE — loaded metadata lags recent writes, and reading a stale sibling row as `Allow` is
|
|
40
|
+
* exactly how three individually-innocent `No Access` edits get through one at a time. And
|
|
41
|
+
* loading them is asynchronous, which `Validate()` is not.
|
|
42
|
+
*
|
|
43
|
+
* Inserts are deliberately not checked: adding a rule can only add access, never remove an
|
|
44
|
+
* existing `Allow`, so the `Deny` check in `Validate()` covers them completely. It has to —
|
|
45
|
+
* snapshot initialization writes its rows one at a time, and an aggregate check would refuse
|
|
46
|
+
* the half-built state.
|
|
47
|
+
*/
|
|
48
|
+
Save(options?: EntitySaveOptions): Promise<boolean>;
|
|
49
|
+
/**
|
|
50
|
+
* Refuses a DELETE that would strip the MJ system user's last `Allow` on a field — the other
|
|
51
|
+
* way, besides editing one to `No Access`, to remove access without writing a `Deny`.
|
|
52
|
+
*
|
|
53
|
+
* Reconciliation's own orphan deletes are unaffected: it only removes rows whose role has lost
|
|
54
|
+
* entity-level read (so the system user is already denied one level up and the guard stands
|
|
55
|
+
* down) or whose field became unrestrictable (forced open regardless).
|
|
56
|
+
*/
|
|
57
|
+
Delete(options?: EntityDeleteOptions): Promise<boolean>;
|
|
58
|
+
/**
|
|
59
|
+
* Records a refusal on the result history so `LatestResult.CompleteMessage` explains it, and
|
|
60
|
+
* returns false — the contract `Save()` and `Delete()` already have for a logical failure.
|
|
61
|
+
*/
|
|
62
|
+
private refuse;
|
|
63
|
+
/**
|
|
64
|
+
* Why the pending change would leave the system user short of its entity-level access, or null.
|
|
65
|
+
*
|
|
66
|
+
* @param removing true when this row is being deleted rather than edited
|
|
67
|
+
*/
|
|
68
|
+
private systemUserAccessLossReason;
|
|
69
|
+
/**
|
|
70
|
+
* Every rule currently stored against this field, straight from the database.
|
|
71
|
+
*
|
|
72
|
+
* `BypassCache` because this decides an access-control question: a cached result that predates
|
|
73
|
+
* a sibling row's edit would answer it with data the guard is specifically trying to catch up
|
|
74
|
+
* with. Inside a transaction this reads the scope's own uncommitted writes, which is what
|
|
75
|
+
* reconciliation needs.
|
|
76
|
+
*/
|
|
77
|
+
private loadCurrentRules;
|
|
78
|
+
/**
|
|
79
|
+
* Why this rule may not target this role, or null when it may.
|
|
80
|
+
*
|
|
81
|
+
* Refuses a **restricting** rule aimed at a role the MJ **system user** holds. The system
|
|
82
|
+
* user is what the server runs background work as: it pre-warms the shared engine caches at
|
|
83
|
+
* startup, and in task mode (job and agent runners) whichever caller touches an engine first
|
|
84
|
+
* configures it for the whole process. Restricting that account does not just restrict it —
|
|
85
|
+
* engines cache their data process-wide, so a partially loaded engine would then serve
|
|
86
|
+
* incomplete records to every user afterward. The damage is silent and nowhere near the rule
|
|
87
|
+
* that caused it.
|
|
88
|
+
*
|
|
89
|
+
* **Only a `Deny` is refused.** Field security has no runtime exemption for any user, so the
|
|
90
|
+
* system user's own access comes from ordinary rows: snapshot initialization writes it
|
|
91
|
+
* `Allow` on every field its roles can read, and this guard is what stops that access being
|
|
92
|
+
* taken away again. `Allow` and `No Access` both save — `Allow` is the grant the server
|
|
93
|
+
* depends on, and `No Access` is neutral, unable to reduce access another role has granted.
|
|
94
|
+
* Refusing those would make it impossible to enable field security on any entity at all,
|
|
95
|
+
* since the standard roles (UI, Developer, Integration) carry entity permissions almost
|
|
96
|
+
* everywhere.
|
|
97
|
+
*
|
|
98
|
+
* This is a guard on CONFIGURATION, not a runtime exemption. What is refused is the
|
|
99
|
+
* arrangement that would make the server unable to do its own work. Take the role off the
|
|
100
|
+
* system user and the rule saves.
|
|
101
|
+
*
|
|
102
|
+
* The database tier already refuses the equivalent arrangement: CodeGen skips a column DENY
|
|
103
|
+
* for any role a service login belongs to, and warns. This is the same rule for the
|
|
104
|
+
* application tier.
|
|
105
|
+
*
|
|
106
|
+
* @param roleID the role the rule targets
|
|
107
|
+
* @param rule the rule's three verbs; omitted only by callers that are pre-checking a role
|
|
108
|
+
* rather than a specific rule, which are answered as though the rule denied
|
|
109
|
+
*/
|
|
110
|
+
static SystemUserRoleRejectionReason(roleID: string | null, rule?: FieldPermissionRuleVerbs): string | null;
|
|
111
|
+
/**
|
|
112
|
+
* Why this rule's Update/Create verbs may not target this field, or null when they may.
|
|
113
|
+
*
|
|
114
|
+
* A **read-only** field cannot be written through the API by anyone: it is excluded from the
|
|
115
|
+
* generated create input type and from the update SET list, and `BaseEntity` never marks it
|
|
116
|
+
* dirty. So an Update or Create verb on it is inert in both directions — a `Deny` prevents
|
|
117
|
+
* nothing that was possible, and an `Allow` grants nothing that was not. Left to save, it
|
|
118
|
+
* reads on screen as a working permission and silently is not one.
|
|
119
|
+
*
|
|
120
|
+
* **Read is untouched, deliberately.** Restricting READ on a read-only field is legitimate
|
|
121
|
+
* and is one of the main things administrators want: foreign-key display columns are
|
|
122
|
+
* read-only, and "hide which client this contract belongs to" is exactly a read restriction
|
|
123
|
+
* on one. Only the two write verbs are refused.
|
|
124
|
+
*
|
|
125
|
+
* Note `EntityFieldInfo.ReadOnly` is the right predicate here, not `IsVirtual`: IS-A parent
|
|
126
|
+
* fields are virtual but ARE writable through the child's save chain, and `ReadOnly` is
|
|
127
|
+
* defined off `AllowUpdateAPI` precisely to tell those apart from joined display columns.
|
|
128
|
+
*/
|
|
129
|
+
static WriteVerbRejectionReason(field: EntityFieldInfo, rule: FieldPermissionRuleVerbs): string | null;
|
|
130
|
+
/**
|
|
131
|
+
* Why this field may not be secured, or null when it may be.
|
|
132
|
+
* Static + field-driven so the same wording can back a future admin-UI pre-check.
|
|
133
|
+
*/
|
|
134
|
+
static RejectionReason(field: EntityFieldInfo): string | null;
|
|
135
|
+
/**
|
|
136
|
+
* Resolves `EntityFieldID` to its {@link EntityFieldInfo} from loaded metadata.
|
|
137
|
+
*
|
|
138
|
+
* Returns null when metadata cannot resolve it — for instance immediately after a schema
|
|
139
|
+
* change, before the metadata cache has refreshed. That case passes validation rather than
|
|
140
|
+
* blocking the administrator: the foreign key already guarantees the row points at a real
|
|
141
|
+
* field, and the runtime aggregation guards hold regardless of what is stored here. Failing
|
|
142
|
+
* closed would trade a real, recurring workflow block for no additional protection.
|
|
143
|
+
*/
|
|
144
|
+
/**
|
|
145
|
+
* Why this role may not carry a field rule on this entity, or null when it may.
|
|
146
|
+
*
|
|
147
|
+
* A field permission REFINES an entity permission — the entity gate decides whether you reach
|
|
148
|
+
* the record at all, and field rules then decide which columns of it you see. A rule bound to a
|
|
149
|
+
* role that has no relationship to the entity is not a refinement of anything, and it is the
|
|
150
|
+
* configuration that made the reconciler's orphan test ambiguous: such a row is fully live at
|
|
151
|
+
* runtime (aggregation matches on role membership alone) while looking inert to any check that
|
|
152
|
+
* asks what the role can do on its own.
|
|
153
|
+
*
|
|
154
|
+
* Requires only that an entity-permission row EXISTS, not that it grants read. A role whose
|
|
155
|
+
* entity permission grants nothing is the ordinary way to express a deny-only carve-out —
|
|
156
|
+
* "these users are normal, minus this column" — and demanding a grant would force an
|
|
157
|
+
* administrator to hand out entity access in order to take a column away. Deleting the entity
|
|
158
|
+
* permission removes the relationship, and reconciliation then cleans up the field rules with
|
|
159
|
+
* it; see `computeOrphanRowIDs`, which tests exactly this condition.
|
|
160
|
+
*
|
|
161
|
+
* Attributed to `RoleID` because that is the value the administrator must change — either pick
|
|
162
|
+
* a role that has entity access, or grant this one an entity permission first.
|
|
163
|
+
*/
|
|
164
|
+
static EntityPermissionRequiredRejectionReason(entity: EntityInfo, roleID: string | null): string | null;
|
|
165
|
+
private resolveTarget;
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=MJEntityFieldPermissionEntityServer.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MJEntityFieldPermissionEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJEntityFieldPermissionEntityServer.server.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,UAAU,EAGV,wBAAwB,EAOxB,gBAAgB,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAG9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBACa,mCAAoC,SAAQ,6BAA6B;IAClE,QAAQ,IAAI,gBAAgB;IAyC5C;;;;;;;;;;;;OAYG;IACmB,IAAI,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IAUzE;;;;;;;OAOG;IACmB,MAAM,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;IAQ7E;;;OAGG;IACH,OAAO,CAAC,MAAM;IAWd;;;;OAIG;YACW,0BAA0B;IA6BxC;;;;;;;OAOG;YACW,gBAAgB;IAyB9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;WACW,6BAA6B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,wBAAwB,GAAG,MAAM,GAAG,IAAI;IA0BlH;;;;;;;;;;;;;;;;;OAiBG;WACW,wBAAwB,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,wBAAwB,GAAG,MAAM,GAAG,IAAI;IAkB7G;;;OAGG;WACW,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI;IAqBpE;;;;;;;;OAQG;IACH;;;;;;;;;;;;;;;;;;;OAmBG;WACW,uCAAuC,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI;IAe/G,OAAO,CAAC,aAAa;CAaxB"}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var MJEntityFieldPermissionEntityServer_1;
|
|
8
|
+
import { BaseEntity, BaseEntityResult, FieldPermissionAccess, IsRestrictingFieldRule, LogError, RunView, ValidationErrorInfo, ValidationErrorType, } from '@memberjunction/core';
|
|
9
|
+
import { RegisterClass, UUIDsEqual } from '@memberjunction/global';
|
|
10
|
+
import { MJEntityFieldPermissionEntity } from '@memberjunction/core-entities';
|
|
11
|
+
import { SystemUserFieldAccessLossReason, SystemUserHoldsRole, UserCache } from '@memberjunction/generic-database-provider';
|
|
12
|
+
/**
|
|
13
|
+
* Server-side `MJ: Entity Field Permissions` entity — the save-time half of the
|
|
14
|
+
* unrestrictable-target guard for field-level security.
|
|
15
|
+
*
|
|
16
|
+
* The runtime aggregation (`EntityFieldInfo.GetUserFieldPermissions`) already forces access
|
|
17
|
+
* open for these targets, so this subclass is not what makes the system safe. It is what makes
|
|
18
|
+
* the system *diagnosable*: without it an administrator can save a row that silently does
|
|
19
|
+
* nothing, then reasonably conclude the feature is broken. Rejecting at save says why.
|
|
20
|
+
*
|
|
21
|
+
* Two categories are refused, matching the two runtime guards exactly:
|
|
22
|
+
*
|
|
23
|
+
* 1. **Primary keys and system columns.** Stripping a primary key from a result breaks entity
|
|
24
|
+
* load, `CompositeKey` construction, relationship resolution, and cache fingerprinting —
|
|
25
|
+
* failures that surface nowhere near the permission record that caused them.
|
|
26
|
+
* 2. **The security-configuration and identity surface** (see
|
|
27
|
+
* {@link EntityFieldInfo.IsOnUnrestrictableEntity}). Restricting a column on the very
|
|
28
|
+
* entities field security is administered through produces a configuration that cannot be
|
|
29
|
+
* reversed through the product — recovery would mean direct SQL against the database.
|
|
30
|
+
*
|
|
31
|
+
* A third category is refused for a different reason: a **`Deny`** aimed at a role the MJ system
|
|
32
|
+
* user holds (see {@link SystemUserRoleRejectionReason}). That one is not about diagnosability —
|
|
33
|
+
* it is what lets the runtime aggregation have no exempt user at all. The server's own account
|
|
34
|
+
* gets its access from ordinary `Allow` rows like everyone else, and this guard is what stops
|
|
35
|
+
* those being revoked.
|
|
36
|
+
*
|
|
37
|
+
* Note what is deliberately NOT here: any notion of a user who is exempt from a Deny at
|
|
38
|
+
* RUNTIME. The guards are on which *targets* are restrictable and on what may be *configured*,
|
|
39
|
+
* never on whose access is evaluated. A feature whose purpose is compensation and donor-giving
|
|
40
|
+
* confidentiality cannot ship with a role that quietly reads everything.
|
|
41
|
+
*/
|
|
42
|
+
let MJEntityFieldPermissionEntityServer = MJEntityFieldPermissionEntityServer_1 = class MJEntityFieldPermissionEntityServer extends MJEntityFieldPermissionEntity {
|
|
43
|
+
Validate() {
|
|
44
|
+
const result = super.Validate();
|
|
45
|
+
const target = this.resolveTarget();
|
|
46
|
+
if (target) {
|
|
47
|
+
const entityPermissionRejection = MJEntityFieldPermissionEntityServer_1.EntityPermissionRequiredRejectionReason(target.Entity, this.RoleID);
|
|
48
|
+
if (entityPermissionRejection) {
|
|
49
|
+
result.Errors.push(new ValidationErrorInfo('RoleID', entityPermissionRejection, this.RoleID, ValidationErrorType.Failure));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const targetField = target?.Field;
|
|
53
|
+
if (targetField) {
|
|
54
|
+
const rejection = MJEntityFieldPermissionEntityServer_1.RejectionReason(targetField);
|
|
55
|
+
if (rejection) {
|
|
56
|
+
result.Errors.push(new ValidationErrorInfo('EntityFieldID', rejection, this.EntityFieldID, ValidationErrorType.Failure));
|
|
57
|
+
}
|
|
58
|
+
const writeVerbRejection = MJEntityFieldPermissionEntityServer_1.WriteVerbRejectionReason(targetField, this);
|
|
59
|
+
if (writeVerbRejection) {
|
|
60
|
+
result.Errors.push(new ValidationErrorInfo('EntityFieldID', writeVerbRejection, this.EntityFieldID, ValidationErrorType.Failure));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const roleRejection = MJEntityFieldPermissionEntityServer_1.SystemUserRoleRejectionReason(this.RoleID, this);
|
|
64
|
+
if (roleRejection) {
|
|
65
|
+
result.Errors.push(new ValidationErrorInfo('RoleID', roleRejection, this.RoleID, ValidationErrorType.Failure));
|
|
66
|
+
}
|
|
67
|
+
result.Success = result.Success && result.Errors.length === 0;
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Refuses an EDIT that would strip the MJ system user's last `Allow` on a field.
|
|
72
|
+
*
|
|
73
|
+
* Not in `Validate()` for two reasons. It needs the field's rules as they stand in the
|
|
74
|
+
* DATABASE — loaded metadata lags recent writes, and reading a stale sibling row as `Allow` is
|
|
75
|
+
* exactly how three individually-innocent `No Access` edits get through one at a time. And
|
|
76
|
+
* loading them is asynchronous, which `Validate()` is not.
|
|
77
|
+
*
|
|
78
|
+
* Inserts are deliberately not checked: adding a rule can only add access, never remove an
|
|
79
|
+
* existing `Allow`, so the `Deny` check in `Validate()` covers them completely. It has to —
|
|
80
|
+
* snapshot initialization writes its rows one at a time, and an aggregate check would refuse
|
|
81
|
+
* the half-built state.
|
|
82
|
+
*/
|
|
83
|
+
async Save(options) {
|
|
84
|
+
if (this.IsSaved) {
|
|
85
|
+
const rejection = await this.systemUserAccessLossReason(false);
|
|
86
|
+
if (rejection) {
|
|
87
|
+
return this.refuse(rejection, 'save');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return super.Save(options);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Refuses a DELETE that would strip the MJ system user's last `Allow` on a field — the other
|
|
94
|
+
* way, besides editing one to `No Access`, to remove access without writing a `Deny`.
|
|
95
|
+
*
|
|
96
|
+
* Reconciliation's own orphan deletes are unaffected: it only removes rows whose role has lost
|
|
97
|
+
* entity-level read (so the system user is already denied one level up and the guard stands
|
|
98
|
+
* down) or whose field became unrestrictable (forced open regardless).
|
|
99
|
+
*/
|
|
100
|
+
async Delete(options) {
|
|
101
|
+
const rejection = await this.systemUserAccessLossReason(true);
|
|
102
|
+
if (rejection) {
|
|
103
|
+
return this.refuse(rejection, 'delete');
|
|
104
|
+
}
|
|
105
|
+
return super.Delete(options);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Records a refusal on the result history so `LatestResult.CompleteMessage` explains it, and
|
|
109
|
+
* returns false — the contract `Save()` and `Delete()` already have for a logical failure.
|
|
110
|
+
*/
|
|
111
|
+
refuse(message, type) {
|
|
112
|
+
const result = new BaseEntityResult();
|
|
113
|
+
result.Success = false;
|
|
114
|
+
result.Type = type === 'delete' ? 'delete' : this.IsSaved ? 'update' : 'create';
|
|
115
|
+
result.Message = message;
|
|
116
|
+
result.StartedAt = new Date();
|
|
117
|
+
result.EndedAt = new Date();
|
|
118
|
+
this.ResultHistory.push(result);
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Why the pending change would leave the system user short of its entity-level access, or null.
|
|
123
|
+
*
|
|
124
|
+
* @param removing true when this row is being deleted rather than edited
|
|
125
|
+
*/
|
|
126
|
+
async systemUserAccessLossReason(removing) {
|
|
127
|
+
// Cheap exit first: the aggregation only ever consults rules bound to a role the user
|
|
128
|
+
// holds, so a change to any other role's rule cannot move the system user's access at all.
|
|
129
|
+
// This is what keeps the query below off the path of ordinary permission administration —
|
|
130
|
+
// it runs only for edits to the handful of roles the server's own account holds.
|
|
131
|
+
if (!SystemUserHoldsRole(this.RoleID)) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const target = this.resolveTarget();
|
|
135
|
+
if (!target) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const current = await this.loadCurrentRules(target.Field.ID);
|
|
139
|
+
const others = current.filter(r => !UUIDsEqual(r.ID, this.ID));
|
|
140
|
+
const projected = removing
|
|
141
|
+
? others
|
|
142
|
+
: [
|
|
143
|
+
...others,
|
|
144
|
+
{
|
|
145
|
+
RoleID: this.RoleID,
|
|
146
|
+
ReadAccess: this.ReadAccess,
|
|
147
|
+
UpdateAccess: this.UpdateAccess,
|
|
148
|
+
CreateAccess: this.CreateAccess,
|
|
149
|
+
},
|
|
150
|
+
];
|
|
151
|
+
return SystemUserFieldAccessLossReason(target.Entity, target.Field, projected);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Every rule currently stored against this field, straight from the database.
|
|
155
|
+
*
|
|
156
|
+
* `BypassCache` because this decides an access-control question: a cached result that predates
|
|
157
|
+
* a sibling row's edit would answer it with data the guard is specifically trying to catch up
|
|
158
|
+
* with. Inside a transaction this reads the scope's own uncommitted writes, which is what
|
|
159
|
+
* reconciliation needs.
|
|
160
|
+
*/
|
|
161
|
+
async loadCurrentRules(entityFieldID) {
|
|
162
|
+
const rv = new RunView(this.RunViewProviderToUse);
|
|
163
|
+
const result = await rv.RunView({
|
|
164
|
+
EntityName: 'MJ: Entity Field Permissions',
|
|
165
|
+
ExtraFilter: `EntityFieldID = '${entityFieldID}'`,
|
|
166
|
+
Fields: ['ID', 'RoleID', 'ReadAccess', 'UpdateAccess', 'CreateAccess'],
|
|
167
|
+
ResultType: 'simple',
|
|
168
|
+
BypassCache: true,
|
|
169
|
+
}, this.ContextCurrentUser);
|
|
170
|
+
if (!result.Success) {
|
|
171
|
+
// Cannot evaluate the guard — say so rather than silently permitting. The caller sees
|
|
172
|
+
// an empty rule set, which never produces a refusal, so this is a fail-open that is at
|
|
173
|
+
// least visible in the log; the startup sweep is the backstop.
|
|
174
|
+
LogError(`[FieldSecurity] Could not load sibling rules for field ${entityFieldID}; ` +
|
|
175
|
+
`the system-user access guard did not run: ${result.ErrorMessage}`);
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
return result.Results ?? [];
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Why this rule may not target this role, or null when it may.
|
|
182
|
+
*
|
|
183
|
+
* Refuses a **restricting** rule aimed at a role the MJ **system user** holds. The system
|
|
184
|
+
* user is what the server runs background work as: it pre-warms the shared engine caches at
|
|
185
|
+
* startup, and in task mode (job and agent runners) whichever caller touches an engine first
|
|
186
|
+
* configures it for the whole process. Restricting that account does not just restrict it —
|
|
187
|
+
* engines cache their data process-wide, so a partially loaded engine would then serve
|
|
188
|
+
* incomplete records to every user afterward. The damage is silent and nowhere near the rule
|
|
189
|
+
* that caused it.
|
|
190
|
+
*
|
|
191
|
+
* **Only a `Deny` is refused.** Field security has no runtime exemption for any user, so the
|
|
192
|
+
* system user's own access comes from ordinary rows: snapshot initialization writes it
|
|
193
|
+
* `Allow` on every field its roles can read, and this guard is what stops that access being
|
|
194
|
+
* taken away again. `Allow` and `No Access` both save — `Allow` is the grant the server
|
|
195
|
+
* depends on, and `No Access` is neutral, unable to reduce access another role has granted.
|
|
196
|
+
* Refusing those would make it impossible to enable field security on any entity at all,
|
|
197
|
+
* since the standard roles (UI, Developer, Integration) carry entity permissions almost
|
|
198
|
+
* everywhere.
|
|
199
|
+
*
|
|
200
|
+
* This is a guard on CONFIGURATION, not a runtime exemption. What is refused is the
|
|
201
|
+
* arrangement that would make the server unable to do its own work. Take the role off the
|
|
202
|
+
* system user and the rule saves.
|
|
203
|
+
*
|
|
204
|
+
* The database tier already refuses the equivalent arrangement: CodeGen skips a column DENY
|
|
205
|
+
* for any role a service login belongs to, and warns. This is the same rule for the
|
|
206
|
+
* application tier.
|
|
207
|
+
*
|
|
208
|
+
* @param roleID the role the rule targets
|
|
209
|
+
* @param rule the rule's three verbs; omitted only by callers that are pre-checking a role
|
|
210
|
+
* rather than a specific rule, which are answered as though the rule denied
|
|
211
|
+
*/
|
|
212
|
+
static SystemUserRoleRejectionReason(roleID, rule) {
|
|
213
|
+
if (!roleID) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
if (rule && !IsRestrictingFieldRule(rule)) {
|
|
217
|
+
return null; // grants and neutrals are exactly what keep the system user working
|
|
218
|
+
}
|
|
219
|
+
// UserCache is populated on the server; a client-side save (or a cold cache) simply
|
|
220
|
+
// skips the check rather than blocking an administrator on missing state.
|
|
221
|
+
const systemUser = UserCache.Instance?.GetSystemUser?.();
|
|
222
|
+
if (!systemUser?.UserRoles?.length) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
const holdsRole = systemUser.UserRoles.some(ur => UUIDsEqual(ur.RoleID, roleID));
|
|
226
|
+
if (!holdsRole) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
const roleName = systemUser.UserRoles.find(ur => UUIDsEqual(ur.RoleID, roleID))?.Role ?? roleID;
|
|
230
|
+
return (`Role '${roleName}' is held by the MJ system user, so it cannot carry a Deny on any field. ` +
|
|
231
|
+
`The server runs background work as that account and shares one engine cache across all users — ` +
|
|
232
|
+
`restricting it would let partially loaded records reach everyone. ` +
|
|
233
|
+
`Remove the role from the system user first, or apply this rule to a different role.`);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Why this rule's Update/Create verbs may not target this field, or null when they may.
|
|
237
|
+
*
|
|
238
|
+
* A **read-only** field cannot be written through the API by anyone: it is excluded from the
|
|
239
|
+
* generated create input type and from the update SET list, and `BaseEntity` never marks it
|
|
240
|
+
* dirty. So an Update or Create verb on it is inert in both directions — a `Deny` prevents
|
|
241
|
+
* nothing that was possible, and an `Allow` grants nothing that was not. Left to save, it
|
|
242
|
+
* reads on screen as a working permission and silently is not one.
|
|
243
|
+
*
|
|
244
|
+
* **Read is untouched, deliberately.** Restricting READ on a read-only field is legitimate
|
|
245
|
+
* and is one of the main things administrators want: foreign-key display columns are
|
|
246
|
+
* read-only, and "hide which client this contract belongs to" is exactly a read restriction
|
|
247
|
+
* on one. Only the two write verbs are refused.
|
|
248
|
+
*
|
|
249
|
+
* Note `EntityFieldInfo.ReadOnly` is the right predicate here, not `IsVirtual`: IS-A parent
|
|
250
|
+
* fields are virtual but ARE writable through the child's save chain, and `ReadOnly` is
|
|
251
|
+
* defined off `AllowUpdateAPI` precisely to tell those apart from joined display columns.
|
|
252
|
+
*/
|
|
253
|
+
static WriteVerbRejectionReason(field, rule) {
|
|
254
|
+
if (!field.ReadOnly) {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
const offending = [];
|
|
258
|
+
if (rule.UpdateAccess !== FieldPermissionAccess.NoAccess)
|
|
259
|
+
offending.push('Update');
|
|
260
|
+
if (rule.CreateAccess !== FieldPermissionAccess.NoAccess)
|
|
261
|
+
offending.push('Create');
|
|
262
|
+
if (offending.length === 0) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
return (`Field '${field.Entity}.${field.Name}' is read-only, so its ${offending.join(' and ')} ` +
|
|
266
|
+
`${offending.length > 1 ? 'permissions have' : 'permission has'} no effect — the field cannot be written ` +
|
|
267
|
+
`through the API by any user. Set ${offending.length > 1 ? 'them' : 'it'} to 'No Access'. ` +
|
|
268
|
+
`Read permission on this field still applies normally.`);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Why this field may not be secured, or null when it may be.
|
|
272
|
+
* Static + field-driven so the same wording can back a future admin-UI pre-check.
|
|
273
|
+
*/
|
|
274
|
+
static RejectionReason(field) {
|
|
275
|
+
if (field.IsUnrestrictableField) {
|
|
276
|
+
const reason = field.IsPrimaryKey || field.IsSoftPrimaryKey ? 'a primary key' : 'a system column';
|
|
277
|
+
return (`Field '${field.Entity}.${field.Name}' is ${reason} and cannot be secured by field-level permissions. ` +
|
|
278
|
+
`Primary keys and system columns must stay readable — removing one from a result breaks entity loading, ` +
|
|
279
|
+
`composite keys, relationship resolution, and cache fingerprinting. Secure the sensitive field itself instead.`);
|
|
280
|
+
}
|
|
281
|
+
if (field.IsOnUnrestrictableEntity) {
|
|
282
|
+
return (`Entity '${field.Entity}' is part of the security configuration and identity surface, so its fields ` +
|
|
283
|
+
`cannot be secured by field-level permissions. Restricting a column here can produce a configuration ` +
|
|
284
|
+
`that cannot be reversed through the application — undoing it would require direct database access.`);
|
|
285
|
+
}
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Resolves `EntityFieldID` to its {@link EntityFieldInfo} from loaded metadata.
|
|
290
|
+
*
|
|
291
|
+
* Returns null when metadata cannot resolve it — for instance immediately after a schema
|
|
292
|
+
* change, before the metadata cache has refreshed. That case passes validation rather than
|
|
293
|
+
* blocking the administrator: the foreign key already guarantees the row points at a real
|
|
294
|
+
* field, and the runtime aggregation guards hold regardless of what is stored here. Failing
|
|
295
|
+
* closed would trade a real, recurring workflow block for no additional protection.
|
|
296
|
+
*/
|
|
297
|
+
/**
|
|
298
|
+
* Why this role may not carry a field rule on this entity, or null when it may.
|
|
299
|
+
*
|
|
300
|
+
* A field permission REFINES an entity permission — the entity gate decides whether you reach
|
|
301
|
+
* the record at all, and field rules then decide which columns of it you see. A rule bound to a
|
|
302
|
+
* role that has no relationship to the entity is not a refinement of anything, and it is the
|
|
303
|
+
* configuration that made the reconciler's orphan test ambiguous: such a row is fully live at
|
|
304
|
+
* runtime (aggregation matches on role membership alone) while looking inert to any check that
|
|
305
|
+
* asks what the role can do on its own.
|
|
306
|
+
*
|
|
307
|
+
* Requires only that an entity-permission row EXISTS, not that it grants read. A role whose
|
|
308
|
+
* entity permission grants nothing is the ordinary way to express a deny-only carve-out —
|
|
309
|
+
* "these users are normal, minus this column" — and demanding a grant would force an
|
|
310
|
+
* administrator to hand out entity access in order to take a column away. Deleting the entity
|
|
311
|
+
* permission removes the relationship, and reconciliation then cleans up the field rules with
|
|
312
|
+
* it; see `computeOrphanRowIDs`, which tests exactly this condition.
|
|
313
|
+
*
|
|
314
|
+
* Attributed to `RoleID` because that is the value the administrator must change — either pick
|
|
315
|
+
* a role that has entity access, or grant this one an entity permission first.
|
|
316
|
+
*/
|
|
317
|
+
static EntityPermissionRequiredRejectionReason(entity, roleID) {
|
|
318
|
+
if (!roleID) {
|
|
319
|
+
return null; // the NOT NULL column is the base class's problem to report, not ours
|
|
320
|
+
}
|
|
321
|
+
if (entity.Permissions.some((p) => UUIDsEqual(p.RoleID, roleID))) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
return (`This role has no entity permission on '${entity.Name}', so a field rule for it would refine nothing. ` +
|
|
325
|
+
`Field permissions narrow the access an entity permission already grants — give the role an entity ` +
|
|
326
|
+
`permission on '${entity.Name}' first (one that grants nothing is fine, and is how a deny-only ` +
|
|
327
|
+
`carve-out is expressed), then set the field rule.`);
|
|
328
|
+
}
|
|
329
|
+
resolveTarget() {
|
|
330
|
+
if (!this.EntityFieldID) {
|
|
331
|
+
return null; // the NOT NULL column is the base class's problem to report, not ours
|
|
332
|
+
}
|
|
333
|
+
const md = this.ProviderToUse;
|
|
334
|
+
for (const entity of md?.Entities ?? []) {
|
|
335
|
+
const match = entity.Fields.find((f) => UUIDsEqual(f.ID, this.EntityFieldID));
|
|
336
|
+
if (match) {
|
|
337
|
+
return { Entity: entity, Field: match };
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
MJEntityFieldPermissionEntityServer = MJEntityFieldPermissionEntityServer_1 = __decorate([
|
|
344
|
+
RegisterClass(BaseEntity, 'MJ: Entity Field Permissions')
|
|
345
|
+
], MJEntityFieldPermissionEntityServer);
|
|
346
|
+
export { MJEntityFieldPermissionEntityServer };
|
|
347
|
+
//# sourceMappingURL=MJEntityFieldPermissionEntityServer.server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MJEntityFieldPermissionEntityServer.server.js","sourceRoot":"","sources":["../../src/custom/MJEntityFieldPermissionEntityServer.server.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityInfo, EntitySaveOptions } from "@memberjunction/core";
|
|
1
|
+
import { EntityInfo, EntitySaveOptions, IEntityDataProvider } from "@memberjunction/core";
|
|
2
2
|
import { MJRemoteOperationEntity, MJRemoteOperationEntity_RemoteOperationLibrary } from "@memberjunction/core-entities";
|
|
3
3
|
/** The shape `GenerateCode()` returns — the AI-authored body + its explanation + the libraries it declared. */
|
|
4
4
|
interface RemoteOperationGeneratedCode {
|
|
@@ -25,7 +25,7 @@ export declare class MJRemoteOperationEntityServer extends MJRemoteOperationEnti
|
|
|
25
25
|
* is unchanged. Reset to false after every save, exactly like Action.ForceCodeGeneration.
|
|
26
26
|
*/
|
|
27
27
|
ForceCodeGeneration: boolean;
|
|
28
|
-
constructor(Entity: EntityInfo);
|
|
28
|
+
constructor(Entity: EntityInfo, Provider?: IEntityDataProvider | null);
|
|
29
29
|
/**
|
|
30
30
|
* Save override: (re)generates the AI body when needed, within a transaction so a generation/save failure
|
|
31
31
|
* leaves no partial state.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MJRemoteOperationEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJRemoteOperationEntityServer.server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,UAAU,EAAE,iBAAiB,EAAsB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"MJRemoteOperationEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJRemoteOperationEntityServer.server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,UAAU,EAAE,iBAAiB,EAAE,mBAAmB,EAAsB,MAAM,sBAAsB,CAAC;AAChJ,OAAO,EAAE,uBAAuB,EAAE,8CAA8C,EAAE,MAAM,+BAA+B,CAAC;AAOxH,+GAA+G;AAC/G,UAAU,4BAA4B;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,8CAA8C,EAAE,CAAC;CAC/D;AAED;;;;;;;;;;;GAWG;AACH,qBACa,6BAA8B,SAAQ,uBAAuB;IACtE;;;OAGG;IACI,mBAAmB,UAAS;gBAEvB,MAAM,EAAE,UAAU,EAAE,QAAQ,GAAE,mBAAmB,GAAG,IAAW;IAW3E;;;OAGG;IACmB,IAAI,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IAiCzE,kIAAkI;IAClI,SAAS,CAAC,gBAAgB,IAAI,OAAO;IAQrC;;;;OAIG;IACU,YAAY,IAAI,OAAO,CAAC,4BAA4B,CAAC;IAqClE,yFAAyF;IACzF,SAAS,CAAC,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IActD,2HAA2H;IAC3H,SAAS,CAAC,wBAAwB,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;CAOtI"}
|
|
@@ -27,14 +27,16 @@ import { DocumentationEngine } from "@memberjunction/doc-utils";
|
|
|
27
27
|
* there is no junction CRUD — `this.LibrariesObject = ...` is the whole "manage libraries" step.
|
|
28
28
|
*/
|
|
29
29
|
let MJRemoteOperationEntityServer = class MJRemoteOperationEntityServer extends MJRemoteOperationEntity {
|
|
30
|
-
constructor(Entity) {
|
|
31
|
-
super(Entity);
|
|
30
|
+
constructor(Entity, Provider = null) {
|
|
31
|
+
super(Entity, Provider);
|
|
32
32
|
/**
|
|
33
33
|
* Transient (never persisted) flag — set it before `Save()` to force regeneration even when `Description`
|
|
34
34
|
* is unchanged. Reset to false after every save, exactly like Action.ForceCodeGeneration.
|
|
35
35
|
*/
|
|
36
36
|
this.ForceCodeGeneration = false;
|
|
37
|
-
//
|
|
37
|
+
// ProviderType is process-wide; this only asserts the server subclass is not
|
|
38
|
+
// loaded in a client bundle. GetEntityObject rebinds the instance provider
|
|
39
|
+
// after construct even if a future edit drops the second argument again.
|
|
38
40
|
const md = new Metadata(); // global-provider-ok: constructor-time provider-type check
|
|
39
41
|
if (md.ProviderType !== 'Database') {
|
|
40
42
|
throw new Error('MJRemoteOperationEntityServer is server/database-only — remove @memberjunction/core-entities-server from client applications.');
|
|
@@ -142,7 +144,7 @@ let MJRemoteOperationEntityServer = class MJRemoteOperationEntityServer extends
|
|
|
142
144
|
MJRemoteOperationEntityServer = __decorate([
|
|
143
145
|
RegisterClass(BaseEntity, 'MJ: Remote Operations') // high priority — ensure this server subclass wins over the generated entity
|
|
144
146
|
,
|
|
145
|
-
__metadata("design:paramtypes", [EntityInfo])
|
|
147
|
+
__metadata("design:paramtypes", [EntityInfo, Object])
|
|
146
148
|
], MJRemoteOperationEntityServer);
|
|
147
149
|
export { MJRemoteOperationEntityServer };
|
|
148
150
|
//# sourceMappingURL=MJRemoteOperationEntityServer.server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MJRemoteOperationEntityServer.server.js","sourceRoot":"","sources":["../../src/custom/MJRemoteOperationEntityServer.server.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAwB,UAAU,
|
|
1
|
+
{"version":3,"file":"MJRemoteOperationEntityServer.server.js","sourceRoot":"","sources":["../../src/custom/MJRemoteOperationEntityServer.server.ts"],"names":[],"mappings":"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"}
|