@litusarchieve18/agricore-utils 1.0.5 → 1.0.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/README.md CHANGED
@@ -117,7 +117,11 @@ Run the publish command:
117
117
  Bash
118
118
  npm publish --access public
119
119
  📝 Version History
120
- v1.0.4refactor the code and edit readme
120
+ v1.0.5add RESOURCE_MODULE_MAP
121
+
122
+ v1.0.6 — adding more logic
123
+
124
+ v1.0.5 — refactor the code and edit readme
121
125
 
122
126
  v1.0.4 — add generateInfrastructureFields, security access control and multi tenant boundary
123
127
 
package/dist/index.d.mts CHANGED
@@ -487,6 +487,7 @@ declare const RESOURCE_PATHS: {
487
487
  teamStaff: string;
488
488
  teamTraining: string;
489
489
  };
490
+ declare const RESOURCE_MODULE_MAP: Record<string, string[]>;
490
491
  declare const ACCESS_RANK: Record<AccessLevelType, number>;
491
492
  declare const ROLE_SECTIONS_BY_KEY: Record<string, UserRoleType[]>;
492
493
  declare function evaluateBaseAccess(resourcePath: string, session: {
@@ -494,7 +495,6 @@ declare function evaluateBaseAccess(resourcePath: string, session: {
494
495
  privileges?: string[];
495
496
  enabledModules?: string[];
496
497
  }, requirements?: {
497
- module?: string;
498
498
  allowedRoles?: UserRoleType[];
499
499
  requiredPrivileges?: string[];
500
500
  }): AccessLevelType;
@@ -506,7 +506,8 @@ declare function evaluateBaseAccess(resourcePath: string, session: {
506
506
  declare function assertTenantBoundary(db: any, requesterCompanyId: string, targets: {
507
507
  targetUserId?: string;
508
508
  authScopeId?: string;
509
+ expectedScopeType?: 'company' | 'legalEntity' | 'facility';
509
510
  }): Promise<boolean>;
510
- declare function resolveEffectiveAccess(base: AccessLevelType, override?: AccessLevelType): AccessLevelType;
511
+ declare function resolveEffectiveAccess(base: AccessLevelType, override?: AccessLevelType | null): AccessLevelType;
511
512
 
512
- export { ACCESS_RANK, ACT, AccessLevel, type AccessLevelType, type AgriCoreSession, AgriLogger, AppError, ApprovalStatus, ApprovalType, AuditStatus, AuditType, type AuthScopeFields, type AvailableScope, CAT, type CompanyConfig, Converter, CropCycleStatus, ERROR_DICTIONARY, EmitterType, EnabledModule, ErrorFactory, FORM_REGISTRY, FacilityType, type FormMeta, HarvestMethod, type InfrastructureFields, LinkedStatus, MOD, MODULE_LABELS, MicroclimateZone, MimeType, type ModCode, NettingType, NotificationMode, OrderStatus, type PermissionLevel, PhysicalState, PlantingMethod, RESOURCE_PATHS, ROLE_SECTIONS_BY_KEY, RelationshipType, type ResolvedError, type ResourceOverrides, RowOrientation, SENTINEL_UUID, type ScopeType, SoilTexture, TASK_TYPE_REGISTRY, type TabConfig, TargetEntityType, TaskStatus, TaskType, type TaskTypeConfig, TransactionType, TrellisType, UserPrivilege, UserRole, type UserRoleType, Validator, VigorRating, WaterSource, assertCanWrite, assertTenantBoundary, buildRlsFilter, canRead, canWrite, evaluateBaseAccess, extractAppCode, generateCanonicalId, generateInfrastructureFields, getAcceptedMimeTypes, getApproveAction, getFormMeta, getFormsGroupedByModule, getTaskTypeOptions, handleAppErrorResponse, isTabVisible, isTabWritable, resolveAccess, resolveAppError, resolveEffectiveAccess, resolveError, resolveMenuVisibility, withAgriLogging };
513
+ export { ACCESS_RANK, ACT, AccessLevel, type AccessLevelType, type AgriCoreSession, AgriLogger, AppError, ApprovalStatus, ApprovalType, AuditStatus, AuditType, type AuthScopeFields, type AvailableScope, CAT, type CompanyConfig, Converter, CropCycleStatus, ERROR_DICTIONARY, EmitterType, EnabledModule, ErrorFactory, FORM_REGISTRY, FacilityType, type FormMeta, HarvestMethod, type InfrastructureFields, LinkedStatus, MOD, MODULE_LABELS, MicroclimateZone, MimeType, type ModCode, NettingType, NotificationMode, OrderStatus, type PermissionLevel, PhysicalState, PlantingMethod, RESOURCE_MODULE_MAP, RESOURCE_PATHS, ROLE_SECTIONS_BY_KEY, RelationshipType, type ResolvedError, type ResourceOverrides, RowOrientation, SENTINEL_UUID, type ScopeType, SoilTexture, TASK_TYPE_REGISTRY, type TabConfig, TargetEntityType, TaskStatus, TaskType, type TaskTypeConfig, TransactionType, TrellisType, UserPrivilege, UserRole, type UserRoleType, Validator, VigorRating, WaterSource, assertCanWrite, assertTenantBoundary, buildRlsFilter, canRead, canWrite, evaluateBaseAccess, extractAppCode, generateCanonicalId, generateInfrastructureFields, getAcceptedMimeTypes, getApproveAction, getFormMeta, getFormsGroupedByModule, getTaskTypeOptions, handleAppErrorResponse, isTabVisible, isTabWritable, resolveAccess, resolveAppError, resolveEffectiveAccess, resolveError, resolveMenuVisibility, withAgriLogging };
package/dist/index.d.ts CHANGED
@@ -487,6 +487,7 @@ declare const RESOURCE_PATHS: {
487
487
  teamStaff: string;
488
488
  teamTraining: string;
489
489
  };
490
+ declare const RESOURCE_MODULE_MAP: Record<string, string[]>;
490
491
  declare const ACCESS_RANK: Record<AccessLevelType, number>;
491
492
  declare const ROLE_SECTIONS_BY_KEY: Record<string, UserRoleType[]>;
492
493
  declare function evaluateBaseAccess(resourcePath: string, session: {
@@ -494,7 +495,6 @@ declare function evaluateBaseAccess(resourcePath: string, session: {
494
495
  privileges?: string[];
495
496
  enabledModules?: string[];
496
497
  }, requirements?: {
497
- module?: string;
498
498
  allowedRoles?: UserRoleType[];
499
499
  requiredPrivileges?: string[];
500
500
  }): AccessLevelType;
@@ -506,7 +506,8 @@ declare function evaluateBaseAccess(resourcePath: string, session: {
506
506
  declare function assertTenantBoundary(db: any, requesterCompanyId: string, targets: {
507
507
  targetUserId?: string;
508
508
  authScopeId?: string;
509
+ expectedScopeType?: 'company' | 'legalEntity' | 'facility';
509
510
  }): Promise<boolean>;
510
- declare function resolveEffectiveAccess(base: AccessLevelType, override?: AccessLevelType): AccessLevelType;
511
+ declare function resolveEffectiveAccess(base: AccessLevelType, override?: AccessLevelType | null): AccessLevelType;
511
512
 
512
- export { ACCESS_RANK, ACT, AccessLevel, type AccessLevelType, type AgriCoreSession, AgriLogger, AppError, ApprovalStatus, ApprovalType, AuditStatus, AuditType, type AuthScopeFields, type AvailableScope, CAT, type CompanyConfig, Converter, CropCycleStatus, ERROR_DICTIONARY, EmitterType, EnabledModule, ErrorFactory, FORM_REGISTRY, FacilityType, type FormMeta, HarvestMethod, type InfrastructureFields, LinkedStatus, MOD, MODULE_LABELS, MicroclimateZone, MimeType, type ModCode, NettingType, NotificationMode, OrderStatus, type PermissionLevel, PhysicalState, PlantingMethod, RESOURCE_PATHS, ROLE_SECTIONS_BY_KEY, RelationshipType, type ResolvedError, type ResourceOverrides, RowOrientation, SENTINEL_UUID, type ScopeType, SoilTexture, TASK_TYPE_REGISTRY, type TabConfig, TargetEntityType, TaskStatus, TaskType, type TaskTypeConfig, TransactionType, TrellisType, UserPrivilege, UserRole, type UserRoleType, Validator, VigorRating, WaterSource, assertCanWrite, assertTenantBoundary, buildRlsFilter, canRead, canWrite, evaluateBaseAccess, extractAppCode, generateCanonicalId, generateInfrastructureFields, getAcceptedMimeTypes, getApproveAction, getFormMeta, getFormsGroupedByModule, getTaskTypeOptions, handleAppErrorResponse, isTabVisible, isTabWritable, resolveAccess, resolveAppError, resolveEffectiveAccess, resolveError, resolveMenuVisibility, withAgriLogging };
513
+ export { ACCESS_RANK, ACT, AccessLevel, type AccessLevelType, type AgriCoreSession, AgriLogger, AppError, ApprovalStatus, ApprovalType, AuditStatus, AuditType, type AuthScopeFields, type AvailableScope, CAT, type CompanyConfig, Converter, CropCycleStatus, ERROR_DICTIONARY, EmitterType, EnabledModule, ErrorFactory, FORM_REGISTRY, FacilityType, type FormMeta, HarvestMethod, type InfrastructureFields, LinkedStatus, MOD, MODULE_LABELS, MicroclimateZone, MimeType, type ModCode, NettingType, NotificationMode, OrderStatus, type PermissionLevel, PhysicalState, PlantingMethod, RESOURCE_MODULE_MAP, RESOURCE_PATHS, ROLE_SECTIONS_BY_KEY, RelationshipType, type ResolvedError, type ResourceOverrides, RowOrientation, SENTINEL_UUID, type ScopeType, SoilTexture, TASK_TYPE_REGISTRY, type TabConfig, TargetEntityType, TaskStatus, TaskType, type TaskTypeConfig, TransactionType, TrellisType, UserPrivilege, UserRole, type UserRoleType, Validator, VigorRating, WaterSource, assertCanWrite, assertTenantBoundary, buildRlsFilter, canRead, canWrite, evaluateBaseAccess, extractAppCode, generateCanonicalId, generateInfrastructureFields, getAcceptedMimeTypes, getApproveAction, getFormMeta, getFormsGroupedByModule, getTaskTypeOptions, handleAppErrorResponse, isTabVisible, isTabWritable, resolveAccess, resolveAppError, resolveEffectiveAccess, resolveError, resolveMenuVisibility, withAgriLogging };
package/dist/index.js CHANGED
@@ -49,6 +49,7 @@ __export(index_exports, {
49
49
  OrderStatus: () => OrderStatus,
50
50
  PhysicalState: () => PhysicalState,
51
51
  PlantingMethod: () => PlantingMethod,
52
+ RESOURCE_MODULE_MAP: () => RESOURCE_MODULE_MAP,
52
53
  RESOURCE_PATHS: () => RESOURCE_PATHS,
53
54
  ROLE_SECTIONS_BY_KEY: () => ROLE_SECTIONS_BY_KEY,
54
55
  RelationshipType: () => RelationshipType,
@@ -842,6 +843,56 @@ var RESOURCE_PATHS = {
842
843
  teamStaff: "team.staff",
843
844
  teamTraining: "team.training"
844
845
  };
846
+ var ALL_OPERATIONS = [
847
+ EnabledModule.FARM_MANAGEMENT,
848
+ EnabledModule.PACKHOUSE,
849
+ EnabledModule.ACCOMMODATION
850
+ ];
851
+ var AGRI_ONLY = [
852
+ EnabledModule.FARM_MANAGEMENT,
853
+ EnabledModule.PACKHOUSE
854
+ ];
855
+ var RESOURCE_MODULE_MAP = {
856
+ // ── Top-level menu sections ────────────────────────────────────────────────
857
+ // CORE features (accessible to everyone with an account)
858
+ [RESOURCE_PATHS.dashboard]: [],
859
+ [RESOURCE_PATHS.operations]: ALL_OPERATIONS,
860
+ [RESOURCE_PATHS.equipment]: ALL_OPERATIONS,
861
+ [RESOURCE_PATHS.safety]: ALL_OPERATIONS,
862
+ [RESOURCE_PATHS.team]: ALL_OPERATIONS,
863
+ [RESOURCE_PATHS.records]: ALL_OPERATIONS,
864
+ [RESOURCE_PATHS.adminSettings]: ALL_OPERATIONS,
865
+ // ── Records & Reports ──────────────────────────────────────────────────────
866
+ [RESOURCE_PATHS.documentHub]: ALL_OPERATIONS,
867
+ [RESOURCE_PATHS.documentHubTemplates]: ALL_OPERATIONS,
868
+ [RESOURCE_PATHS.documentHubFolder]: ALL_OPERATIONS,
869
+ [RESOURCE_PATHS.documentHubExtraction]: ALL_OPERATIONS,
870
+ [RESOURCE_PATHS.documentHubQueue]: ALL_OPERATIONS,
871
+ [RESOURCE_PATHS.analytics]: ALL_OPERATIONS,
872
+ // ── Admin Settings ─────────────────────────────────────────────────────────
873
+ [RESOURCE_PATHS.adminUserRoster]: ALL_OPERATIONS,
874
+ [RESOURCE_PATHS.adminBlueprintsSites]: ALL_OPERATIONS,
875
+ [RESOURCE_PATHS.adminRolesPermissions]: ALL_OPERATIONS,
876
+ [RESOURCE_PATHS.adminCompanySettings]: ALL_OPERATIONS,
877
+ // ── Operations ────────────────────────────────────────────────────────────
878
+ [RESOURCE_PATHS.farmManagement]: [EnabledModule.FARM_MANAGEMENT],
879
+ [RESOURCE_PATHS.packhouse]: [EnabledModule.PACKHOUSE],
880
+ [RESOURCE_PATHS.accommodation]: [EnabledModule.ACCOMMODATION],
881
+ // ── Equipment & Fleet ──────────────────────────────────────────────────────
882
+ [RESOURCE_PATHS.equipPreStart]: ALL_OPERATIONS,
883
+ [RESOURCE_PATHS.equipMaintenance]: ALL_OPERATIONS,
884
+ [RESOURCE_PATHS.equipFuelRegister]: ALL_OPERATIONS,
885
+ [RESOURCE_PATHS.equipCalibration]: AGRI_ONLY,
886
+ [RESOURCE_PATHS.equipFleet]: ALL_OPERATIONS,
887
+ // ── Safety, Quality & Compliance ──────────────────────────────────────────
888
+ [RESOURCE_PATHS.safetyAuditing]: ALL_OPERATIONS,
889
+ [RESOURCE_PATHS.safetyFood]: AGRI_ONLY,
890
+ [RESOURCE_PATHS.safetyWhs]: ALL_OPERATIONS,
891
+ [RESOURCE_PATHS.safetyCleaning]: ALL_OPERATIONS,
892
+ // ── Team & HR ──────────────────────────────────────────────────────────────
893
+ [RESOURCE_PATHS.teamStaff]: ALL_OPERATIONS,
894
+ [RESOURCE_PATHS.teamTraining]: ALL_OPERATIONS
895
+ };
845
896
  var ACCESS_RANK = {
846
897
  NONE: 0,
847
898
  READ: 1,
@@ -859,18 +910,20 @@ var ROLE_SECTIONS_BY_KEY = {
859
910
  function evaluateBaseAccess(resourcePath, session, requirements = {}) {
860
911
  if (!session?.role) return "NONE";
861
912
  if (session.role === UserRole.OWNER) return "WRITE";
913
+ const requiredModules = RESOURCE_MODULE_MAP[resourcePath] || [];
914
+ if (requiredModules.length > 0) {
915
+ const hasModule = requiredModules.some((m) => session.enabledModules?.includes(m));
916
+ if (!hasModule) return "NONE";
917
+ }
862
918
  const topLevel = resourcePath.split(".")[0];
863
919
  const defaultRoles = ROLE_SECTIONS_BY_KEY[topLevel] || [];
864
- if (requirements.module && !session.enabledModules?.includes(requirements.module)) return "NONE";
865
920
  const allowedRoles = requirements.allowedRoles?.length ? requirements.allowedRoles : defaultRoles;
866
921
  if (!allowedRoles.includes(session.role)) return "NONE";
867
922
  if (requirements.requiredPrivileges?.length) {
868
923
  const hasPrivilege = requirements.requiredPrivileges.some((p) => session.privileges?.includes(p));
869
924
  if (!hasPrivilege) return "NONE";
870
925
  }
871
- const highRoles = [UserRole.OWNER, UserRole.MANAGER];
872
- const isHighRole = highRoles.includes(session.role);
873
- return isHighRole ? "WRITE" : "READ";
926
+ return session.role === UserRole.MANAGER ? "WRITE" : "READ";
874
927
  }
875
928
  async function assertTenantBoundary(db, requesterCompanyId, targets) {
876
929
  if (!requesterCompanyId) {
@@ -887,23 +940,29 @@ async function assertTenantBoundary(db, requesterCompanyId, targets) {
887
940
  }
888
941
  }
889
942
  if (targets.authScopeId) {
890
- const [companyMatch, leMatch, facMatch] = await Promise.all([
891
- db.Company.filter({ canonicalId: targets.authScopeId, id: requesterCompanyId }),
892
- // Base44 id must match requester
893
- db.LegalEntity.filter({ canonicalId: targets.authScopeId, companyId: requesterCompanyId }),
894
- db.Facility.filter({ canonicalId: targets.authScopeId, companyId: requesterCompanyId })
895
- ]);
896
- const isValidScope = companyMatch.length > 0 || leMatch.length > 0 || facMatch.length > 0;
897
- if (!isValidScope) {
898
- console.error(`[SECURITY BREACH ATTEMPT] User tried to assign scope ${targets.authScopeId} outside company ${requesterCompanyId}`);
899
- throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Invalid authorization scope or tenant boundary violation.");
943
+ let matchFound = false;
944
+ if (targets.expectedScopeType === "company" || !targets.expectedScopeType) {
945
+ const c = await db.Company.filter({ canonicalId: targets.authScopeId, id: requesterCompanyId });
946
+ if (c.length > 0) matchFound = true;
947
+ }
948
+ if (!matchFound && (targets.expectedScopeType === "legalEntity" || !targets.expectedScopeType)) {
949
+ const le = await db.LegalEntity.filter({ canonicalId: targets.authScopeId, companyId: requesterCompanyId });
950
+ if (le.length > 0) matchFound = true;
951
+ }
952
+ if (!matchFound && (targets.expectedScopeType === "facility" || !targets.expectedScopeType)) {
953
+ const fac = await db.Facility.filter({ canonicalId: targets.authScopeId, companyId: requesterCompanyId });
954
+ if (fac.length > 0) matchFound = true;
955
+ }
956
+ if (!matchFound) {
957
+ console.error(`[SECURITY BREACH/VALIDATION ERROR] Scope ${targets.authScopeId} not found for type ${targets.expectedScopeType} in company ${requesterCompanyId}`);
958
+ throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Invalid authorization scope level or tenant boundary violation.");
900
959
  }
901
960
  }
902
961
  return true;
903
962
  }
904
963
  function resolveEffectiveAccess(base, override) {
905
- if (!override) return base;
906
- return ACCESS_RANK[override] >= ACCESS_RANK[base] ? override : base;
964
+ if (override === void 0 || override === null) return base;
965
+ return override;
907
966
  }
908
967
  // Annotate the CommonJS export names for ESM import in node:
909
968
  0 && (module.exports = {
@@ -936,6 +995,7 @@ function resolveEffectiveAccess(base, override) {
936
995
  OrderStatus,
937
996
  PhysicalState,
938
997
  PlantingMethod,
998
+ RESOURCE_MODULE_MAP,
939
999
  RESOURCE_PATHS,
940
1000
  ROLE_SECTIONS_BY_KEY,
941
1001
  RelationshipType,
package/dist/index.mjs CHANGED
@@ -748,6 +748,56 @@ var RESOURCE_PATHS = {
748
748
  teamStaff: "team.staff",
749
749
  teamTraining: "team.training"
750
750
  };
751
+ var ALL_OPERATIONS = [
752
+ EnabledModule.FARM_MANAGEMENT,
753
+ EnabledModule.PACKHOUSE,
754
+ EnabledModule.ACCOMMODATION
755
+ ];
756
+ var AGRI_ONLY = [
757
+ EnabledModule.FARM_MANAGEMENT,
758
+ EnabledModule.PACKHOUSE
759
+ ];
760
+ var RESOURCE_MODULE_MAP = {
761
+ // ── Top-level menu sections ────────────────────────────────────────────────
762
+ // CORE features (accessible to everyone with an account)
763
+ [RESOURCE_PATHS.dashboard]: [],
764
+ [RESOURCE_PATHS.operations]: ALL_OPERATIONS,
765
+ [RESOURCE_PATHS.equipment]: ALL_OPERATIONS,
766
+ [RESOURCE_PATHS.safety]: ALL_OPERATIONS,
767
+ [RESOURCE_PATHS.team]: ALL_OPERATIONS,
768
+ [RESOURCE_PATHS.records]: ALL_OPERATIONS,
769
+ [RESOURCE_PATHS.adminSettings]: ALL_OPERATIONS,
770
+ // ── Records & Reports ──────────────────────────────────────────────────────
771
+ [RESOURCE_PATHS.documentHub]: ALL_OPERATIONS,
772
+ [RESOURCE_PATHS.documentHubTemplates]: ALL_OPERATIONS,
773
+ [RESOURCE_PATHS.documentHubFolder]: ALL_OPERATIONS,
774
+ [RESOURCE_PATHS.documentHubExtraction]: ALL_OPERATIONS,
775
+ [RESOURCE_PATHS.documentHubQueue]: ALL_OPERATIONS,
776
+ [RESOURCE_PATHS.analytics]: ALL_OPERATIONS,
777
+ // ── Admin Settings ─────────────────────────────────────────────────────────
778
+ [RESOURCE_PATHS.adminUserRoster]: ALL_OPERATIONS,
779
+ [RESOURCE_PATHS.adminBlueprintsSites]: ALL_OPERATIONS,
780
+ [RESOURCE_PATHS.adminRolesPermissions]: ALL_OPERATIONS,
781
+ [RESOURCE_PATHS.adminCompanySettings]: ALL_OPERATIONS,
782
+ // ── Operations ────────────────────────────────────────────────────────────
783
+ [RESOURCE_PATHS.farmManagement]: [EnabledModule.FARM_MANAGEMENT],
784
+ [RESOURCE_PATHS.packhouse]: [EnabledModule.PACKHOUSE],
785
+ [RESOURCE_PATHS.accommodation]: [EnabledModule.ACCOMMODATION],
786
+ // ── Equipment & Fleet ──────────────────────────────────────────────────────
787
+ [RESOURCE_PATHS.equipPreStart]: ALL_OPERATIONS,
788
+ [RESOURCE_PATHS.equipMaintenance]: ALL_OPERATIONS,
789
+ [RESOURCE_PATHS.equipFuelRegister]: ALL_OPERATIONS,
790
+ [RESOURCE_PATHS.equipCalibration]: AGRI_ONLY,
791
+ [RESOURCE_PATHS.equipFleet]: ALL_OPERATIONS,
792
+ // ── Safety, Quality & Compliance ──────────────────────────────────────────
793
+ [RESOURCE_PATHS.safetyAuditing]: ALL_OPERATIONS,
794
+ [RESOURCE_PATHS.safetyFood]: AGRI_ONLY,
795
+ [RESOURCE_PATHS.safetyWhs]: ALL_OPERATIONS,
796
+ [RESOURCE_PATHS.safetyCleaning]: ALL_OPERATIONS,
797
+ // ── Team & HR ──────────────────────────────────────────────────────────────
798
+ [RESOURCE_PATHS.teamStaff]: ALL_OPERATIONS,
799
+ [RESOURCE_PATHS.teamTraining]: ALL_OPERATIONS
800
+ };
751
801
  var ACCESS_RANK = {
752
802
  NONE: 0,
753
803
  READ: 1,
@@ -765,18 +815,20 @@ var ROLE_SECTIONS_BY_KEY = {
765
815
  function evaluateBaseAccess(resourcePath, session, requirements = {}) {
766
816
  if (!session?.role) return "NONE";
767
817
  if (session.role === UserRole.OWNER) return "WRITE";
818
+ const requiredModules = RESOURCE_MODULE_MAP[resourcePath] || [];
819
+ if (requiredModules.length > 0) {
820
+ const hasModule = requiredModules.some((m) => session.enabledModules?.includes(m));
821
+ if (!hasModule) return "NONE";
822
+ }
768
823
  const topLevel = resourcePath.split(".")[0];
769
824
  const defaultRoles = ROLE_SECTIONS_BY_KEY[topLevel] || [];
770
- if (requirements.module && !session.enabledModules?.includes(requirements.module)) return "NONE";
771
825
  const allowedRoles = requirements.allowedRoles?.length ? requirements.allowedRoles : defaultRoles;
772
826
  if (!allowedRoles.includes(session.role)) return "NONE";
773
827
  if (requirements.requiredPrivileges?.length) {
774
828
  const hasPrivilege = requirements.requiredPrivileges.some((p) => session.privileges?.includes(p));
775
829
  if (!hasPrivilege) return "NONE";
776
830
  }
777
- const highRoles = [UserRole.OWNER, UserRole.MANAGER];
778
- const isHighRole = highRoles.includes(session.role);
779
- return isHighRole ? "WRITE" : "READ";
831
+ return session.role === UserRole.MANAGER ? "WRITE" : "READ";
780
832
  }
781
833
  async function assertTenantBoundary(db, requesterCompanyId, targets) {
782
834
  if (!requesterCompanyId) {
@@ -793,23 +845,29 @@ async function assertTenantBoundary(db, requesterCompanyId, targets) {
793
845
  }
794
846
  }
795
847
  if (targets.authScopeId) {
796
- const [companyMatch, leMatch, facMatch] = await Promise.all([
797
- db.Company.filter({ canonicalId: targets.authScopeId, id: requesterCompanyId }),
798
- // Base44 id must match requester
799
- db.LegalEntity.filter({ canonicalId: targets.authScopeId, companyId: requesterCompanyId }),
800
- db.Facility.filter({ canonicalId: targets.authScopeId, companyId: requesterCompanyId })
801
- ]);
802
- const isValidScope = companyMatch.length > 0 || leMatch.length > 0 || facMatch.length > 0;
803
- if (!isValidScope) {
804
- console.error(`[SECURITY BREACH ATTEMPT] User tried to assign scope ${targets.authScopeId} outside company ${requesterCompanyId}`);
805
- throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Invalid authorization scope or tenant boundary violation.");
848
+ let matchFound = false;
849
+ if (targets.expectedScopeType === "company" || !targets.expectedScopeType) {
850
+ const c = await db.Company.filter({ canonicalId: targets.authScopeId, id: requesterCompanyId });
851
+ if (c.length > 0) matchFound = true;
852
+ }
853
+ if (!matchFound && (targets.expectedScopeType === "legalEntity" || !targets.expectedScopeType)) {
854
+ const le = await db.LegalEntity.filter({ canonicalId: targets.authScopeId, companyId: requesterCompanyId });
855
+ if (le.length > 0) matchFound = true;
856
+ }
857
+ if (!matchFound && (targets.expectedScopeType === "facility" || !targets.expectedScopeType)) {
858
+ const fac = await db.Facility.filter({ canonicalId: targets.authScopeId, companyId: requesterCompanyId });
859
+ if (fac.length > 0) matchFound = true;
860
+ }
861
+ if (!matchFound) {
862
+ console.error(`[SECURITY BREACH/VALIDATION ERROR] Scope ${targets.authScopeId} not found for type ${targets.expectedScopeType} in company ${requesterCompanyId}`);
863
+ throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Invalid authorization scope level or tenant boundary violation.");
806
864
  }
807
865
  }
808
866
  return true;
809
867
  }
810
868
  function resolveEffectiveAccess(base, override) {
811
- if (!override) return base;
812
- return ACCESS_RANK[override] >= ACCESS_RANK[base] ? override : base;
869
+ if (override === void 0 || override === null) return base;
870
+ return override;
813
871
  }
814
872
  export {
815
873
  ACCESS_RANK,
@@ -841,6 +899,7 @@ export {
841
899
  OrderStatus,
842
900
  PhysicalState,
843
901
  PlantingMethod,
902
+ RESOURCE_MODULE_MAP,
844
903
  RESOURCE_PATHS,
845
904
  ROLE_SECTIONS_BY_KEY,
846
905
  RelationshipType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litusarchieve18/agricore-utils",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Canonical Backend Utility Library for AgriCore",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",