@wtfalch/authz-store 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +335 -5
- package/dist/activations.d.ts +60 -0
- package/dist/activations.js +516 -0
- package/dist/alerts.d.ts +86 -0
- package/dist/alerts.js +132 -0
- package/dist/assignments.d.ts +71 -0
- package/dist/assignments.js +103 -0
- package/dist/audit.d.ts +122 -0
- package/dist/audit.js +194 -0
- package/dist/binding.d.ts +17 -0
- package/dist/binding.js +8 -0
- package/dist/boot.d.ts +54 -0
- package/dist/boot.js +143 -0
- package/dist/bootstrap.d.ts +28 -0
- package/dist/bootstrap.js +83 -0
- package/dist/break-glass.d.ts +61 -0
- package/dist/break-glass.js +247 -0
- package/dist/credentials.d.ts +94 -0
- package/dist/credentials.js +230 -0
- package/dist/denial.d.ts +9 -0
- package/dist/denial.js +49 -0
- package/dist/erase.d.ts +58 -0
- package/dist/erase.js +108 -0
- package/dist/events.d.ts +77 -0
- package/dist/events.js +107 -0
- package/dist/export.d.ts +161 -0
- package/dist/export.js +293 -0
- package/dist/grants.d.ts +2 -0
- package/dist/index.d.ts +33 -1
- package/dist/index.js +33 -1
- package/dist/install-owner.d.ts +25 -0
- package/dist/install-owner.js +159 -0
- package/dist/invitations.d.ts +160 -0
- package/dist/invitations.js +685 -0
- package/dist/membership-rows.d.ts +206 -0
- package/dist/membership-rows.js +271 -0
- package/dist/memberships.d.ts +87 -0
- package/dist/memberships.js +272 -0
- package/dist/nesting.d.ts +124 -0
- package/dist/nesting.js +515 -0
- package/dist/person-records.d.ts +186 -0
- package/dist/person-records.js +263 -0
- package/dist/platform.d.ts +20 -0
- package/dist/platform.js +65 -0
- package/dist/policy-access.d.ts +260 -0
- package/dist/policy-access.js +348 -0
- package/dist/policy-resources.d.ts +5 -0
- package/dist/policy-resources.js +46 -0
- package/dist/policy-schema.d.ts +449 -0
- package/dist/policy-schema.js +63 -0
- package/dist/policy.d.ts +64 -0
- package/dist/policy.js +65 -0
- package/dist/propagate.d.ts +43 -0
- package/dist/propagate.js +47 -0
- package/dist/reconcile.d.ts +78 -0
- package/dist/reconcile.js +94 -0
- package/dist/resource-access.d.ts +344 -0
- package/dist/resource-access.js +656 -0
- package/dist/role-keys.d.ts +9 -0
- package/dist/role-keys.js +9 -0
- package/dist/roles.d.ts +36 -0
- package/dist/roles.js +191 -0
- package/dist/schema.d.ts +18 -1
- package/dist/schema.js +8 -1
- package/dist/startup.d.ts +57 -0
- package/dist/startup.js +113 -0
- package/dist/tenants.d.ts +213 -0
- package/dist/tenants.js +808 -0
- package/dist/tree-writes.d.ts +65 -0
- package/dist/tree-writes.js +201 -0
- package/dist/tree.d.ts +272 -0
- package/dist/tree.js +565 -0
- package/dist/types.d.ts +87 -0
- package/dist/types.js +15 -0
- package/migrations/0003_product_tenant_kind.sql +14 -0
- package/migrations/0004_credential_keys_issued_id.sql +33 -0
- package/migrations/0005_activations.sql +71 -0
- package/migrations/0006_erase_person.sql +134 -0
- package/package.json +9 -4
package/dist/policy-schema.d.ts
CHANGED
|
@@ -166,6 +166,7 @@ export declare const policyRoles: import("drizzle-orm/pg-core").PgTableWithColum
|
|
|
166
166
|
boundary: {
|
|
167
167
|
kind: "organisation";
|
|
168
168
|
organisationId: string;
|
|
169
|
+
descendants?: boolean | undefined;
|
|
169
170
|
} | {
|
|
170
171
|
kind: "organisations";
|
|
171
172
|
organisationIds: string[];
|
|
@@ -204,6 +205,7 @@ export declare const policyRoles: import("drizzle-orm/pg-core").PgTableWithColum
|
|
|
204
205
|
boundary: {
|
|
205
206
|
kind: "organisation";
|
|
206
207
|
organisationId: string;
|
|
208
|
+
descendants?: boolean | undefined;
|
|
207
209
|
} | {
|
|
208
210
|
kind: "organisations";
|
|
209
211
|
organisationIds: string[];
|
|
@@ -507,6 +509,7 @@ export declare const policyAssignments: import("drizzle-orm/pg-core").PgTableWit
|
|
|
507
509
|
data: {
|
|
508
510
|
kind: "organisation";
|
|
509
511
|
organisationId: string;
|
|
512
|
+
descendants?: boolean | undefined;
|
|
510
513
|
} | {
|
|
511
514
|
kind: "organisations";
|
|
512
515
|
organisationIds: string[];
|
|
@@ -527,6 +530,7 @@ export declare const policyAssignments: import("drizzle-orm/pg-core").PgTableWit
|
|
|
527
530
|
$type: {
|
|
528
531
|
kind: "organisation";
|
|
529
532
|
organisationId: string;
|
|
533
|
+
descendants?: boolean | undefined;
|
|
530
534
|
} | {
|
|
531
535
|
kind: "organisations";
|
|
532
536
|
organisationIds: string[];
|
|
@@ -684,6 +688,451 @@ export declare const policyAssignments: import("drizzle-orm/pg-core").PgTableWit
|
|
|
684
688
|
};
|
|
685
689
|
dialect: "pg";
|
|
686
690
|
}>;
|
|
691
|
+
/**
|
|
692
|
+
* A human principal's time-boxed request to exercise a subset of the
|
|
693
|
+
* ELEVATED permissions they are otherwise only eligible for (ADR 0016, "No
|
|
694
|
+
* standing authority"). Ported from Boule's `drizzle/0014_activations.sql`
|
|
695
|
+
* (authz#83 wave 11b, `migrations/0005_activations.sql` here);
|
|
696
|
+
* `authz_roles.updated_by` above and the `'activation'`
|
|
697
|
+
* `authz_assignments.source` value that same source file added are already
|
|
698
|
+
* in `0001_baseline.sql`, so only these two tables land in the new file.
|
|
699
|
+
*/
|
|
700
|
+
export declare const policyActivations: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
701
|
+
name: "authz_activations";
|
|
702
|
+
schema: undefined;
|
|
703
|
+
columns: {
|
|
704
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
705
|
+
name: "id";
|
|
706
|
+
tableName: "authz_activations";
|
|
707
|
+
dataType: "string";
|
|
708
|
+
columnType: "PgUUID";
|
|
709
|
+
data: string;
|
|
710
|
+
driverParam: string;
|
|
711
|
+
notNull: true;
|
|
712
|
+
hasDefault: true;
|
|
713
|
+
isPrimaryKey: true;
|
|
714
|
+
isAutoincrement: false;
|
|
715
|
+
hasRuntimeDefault: false;
|
|
716
|
+
enumValues: undefined;
|
|
717
|
+
baseColumn: never;
|
|
718
|
+
identity: undefined;
|
|
719
|
+
generated: undefined;
|
|
720
|
+
}, {}, {}>;
|
|
721
|
+
tenantId: import("drizzle-orm/pg-core").PgColumn<{
|
|
722
|
+
name: "tenant_id";
|
|
723
|
+
tableName: "authz_activations";
|
|
724
|
+
dataType: "string";
|
|
725
|
+
columnType: "PgUUID";
|
|
726
|
+
data: string;
|
|
727
|
+
driverParam: string;
|
|
728
|
+
notNull: true;
|
|
729
|
+
hasDefault: false;
|
|
730
|
+
isPrimaryKey: false;
|
|
731
|
+
isAutoincrement: false;
|
|
732
|
+
hasRuntimeDefault: false;
|
|
733
|
+
enumValues: undefined;
|
|
734
|
+
baseColumn: never;
|
|
735
|
+
identity: undefined;
|
|
736
|
+
generated: undefined;
|
|
737
|
+
}, {}, {}>;
|
|
738
|
+
applicationId: import("drizzle-orm/pg-core").PgColumn<{
|
|
739
|
+
name: "application_id";
|
|
740
|
+
tableName: "authz_activations";
|
|
741
|
+
dataType: "string";
|
|
742
|
+
columnType: "PgText";
|
|
743
|
+
data: string;
|
|
744
|
+
driverParam: string;
|
|
745
|
+
notNull: true;
|
|
746
|
+
hasDefault: false;
|
|
747
|
+
isPrimaryKey: false;
|
|
748
|
+
isAutoincrement: false;
|
|
749
|
+
hasRuntimeDefault: false;
|
|
750
|
+
enumValues: [string, ...string[]];
|
|
751
|
+
baseColumn: never;
|
|
752
|
+
identity: undefined;
|
|
753
|
+
generated: undefined;
|
|
754
|
+
}, {}, {}>;
|
|
755
|
+
platformId: import("drizzle-orm/pg-core").PgColumn<{
|
|
756
|
+
name: "platform_id";
|
|
757
|
+
tableName: "authz_activations";
|
|
758
|
+
dataType: "string";
|
|
759
|
+
columnType: "PgText";
|
|
760
|
+
data: string;
|
|
761
|
+
driverParam: string;
|
|
762
|
+
notNull: true;
|
|
763
|
+
hasDefault: false;
|
|
764
|
+
isPrimaryKey: false;
|
|
765
|
+
isAutoincrement: false;
|
|
766
|
+
hasRuntimeDefault: false;
|
|
767
|
+
enumValues: [string, ...string[]];
|
|
768
|
+
baseColumn: never;
|
|
769
|
+
identity: undefined;
|
|
770
|
+
generated: undefined;
|
|
771
|
+
}, {}, {}>;
|
|
772
|
+
principalId: import("drizzle-orm/pg-core").PgColumn<{
|
|
773
|
+
name: "principal_id";
|
|
774
|
+
tableName: "authz_activations";
|
|
775
|
+
dataType: "string";
|
|
776
|
+
columnType: "PgText";
|
|
777
|
+
data: string;
|
|
778
|
+
driverParam: string;
|
|
779
|
+
notNull: true;
|
|
780
|
+
hasDefault: false;
|
|
781
|
+
isPrimaryKey: false;
|
|
782
|
+
isAutoincrement: false;
|
|
783
|
+
hasRuntimeDefault: false;
|
|
784
|
+
enumValues: [string, ...string[]];
|
|
785
|
+
baseColumn: never;
|
|
786
|
+
identity: undefined;
|
|
787
|
+
generated: undefined;
|
|
788
|
+
}, {}, {}>;
|
|
789
|
+
principalClass: import("drizzle-orm/pg-core").PgColumn<{
|
|
790
|
+
name: "principal_class";
|
|
791
|
+
tableName: "authz_activations";
|
|
792
|
+
dataType: "string";
|
|
793
|
+
columnType: "PgText";
|
|
794
|
+
data: string;
|
|
795
|
+
driverParam: string;
|
|
796
|
+
notNull: true;
|
|
797
|
+
hasDefault: false;
|
|
798
|
+
isPrimaryKey: false;
|
|
799
|
+
isAutoincrement: false;
|
|
800
|
+
hasRuntimeDefault: false;
|
|
801
|
+
enumValues: [string, ...string[]];
|
|
802
|
+
baseColumn: never;
|
|
803
|
+
identity: undefined;
|
|
804
|
+
generated: undefined;
|
|
805
|
+
}, {}, {}>;
|
|
806
|
+
permissions: import("drizzle-orm/pg-core").PgColumn<{
|
|
807
|
+
name: "permissions";
|
|
808
|
+
tableName: "authz_activations";
|
|
809
|
+
dataType: "array";
|
|
810
|
+
columnType: "PgArray";
|
|
811
|
+
data: string[];
|
|
812
|
+
driverParam: string | string[];
|
|
813
|
+
notNull: true;
|
|
814
|
+
hasDefault: false;
|
|
815
|
+
isPrimaryKey: false;
|
|
816
|
+
isAutoincrement: false;
|
|
817
|
+
hasRuntimeDefault: false;
|
|
818
|
+
enumValues: [string, ...string[]];
|
|
819
|
+
baseColumn: import("drizzle-orm").Column<{
|
|
820
|
+
name: "permissions";
|
|
821
|
+
tableName: "authz_activations";
|
|
822
|
+
dataType: "string";
|
|
823
|
+
columnType: "PgText";
|
|
824
|
+
data: string;
|
|
825
|
+
driverParam: string;
|
|
826
|
+
notNull: false;
|
|
827
|
+
hasDefault: false;
|
|
828
|
+
isPrimaryKey: false;
|
|
829
|
+
isAutoincrement: false;
|
|
830
|
+
hasRuntimeDefault: false;
|
|
831
|
+
enumValues: [string, ...string[]];
|
|
832
|
+
baseColumn: never;
|
|
833
|
+
identity: undefined;
|
|
834
|
+
generated: undefined;
|
|
835
|
+
}, {}, {}>;
|
|
836
|
+
identity: undefined;
|
|
837
|
+
generated: undefined;
|
|
838
|
+
}, {}, {
|
|
839
|
+
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
840
|
+
name: "permissions";
|
|
841
|
+
dataType: "string";
|
|
842
|
+
columnType: "PgText";
|
|
843
|
+
data: string;
|
|
844
|
+
enumValues: [string, ...string[]];
|
|
845
|
+
driverParam: string;
|
|
846
|
+
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
847
|
+
size: undefined;
|
|
848
|
+
}>;
|
|
849
|
+
reason: import("drizzle-orm/pg-core").PgColumn<{
|
|
850
|
+
name: "reason";
|
|
851
|
+
tableName: "authz_activations";
|
|
852
|
+
dataType: "string";
|
|
853
|
+
columnType: "PgText";
|
|
854
|
+
data: string;
|
|
855
|
+
driverParam: string;
|
|
856
|
+
notNull: true;
|
|
857
|
+
hasDefault: false;
|
|
858
|
+
isPrimaryKey: false;
|
|
859
|
+
isAutoincrement: false;
|
|
860
|
+
hasRuntimeDefault: false;
|
|
861
|
+
enumValues: [string, ...string[]];
|
|
862
|
+
baseColumn: never;
|
|
863
|
+
identity: undefined;
|
|
864
|
+
generated: undefined;
|
|
865
|
+
}, {}, {}>;
|
|
866
|
+
reference: import("drizzle-orm/pg-core").PgColumn<{
|
|
867
|
+
name: "reference";
|
|
868
|
+
tableName: "authz_activations";
|
|
869
|
+
dataType: "string";
|
|
870
|
+
columnType: "PgText";
|
|
871
|
+
data: string;
|
|
872
|
+
driverParam: string;
|
|
873
|
+
notNull: true;
|
|
874
|
+
hasDefault: false;
|
|
875
|
+
isPrimaryKey: false;
|
|
876
|
+
isAutoincrement: false;
|
|
877
|
+
hasRuntimeDefault: false;
|
|
878
|
+
enumValues: [string, ...string[]];
|
|
879
|
+
baseColumn: never;
|
|
880
|
+
identity: undefined;
|
|
881
|
+
generated: undefined;
|
|
882
|
+
}, {}, {}>;
|
|
883
|
+
status: import("drizzle-orm/pg-core").PgColumn<{
|
|
884
|
+
name: "status";
|
|
885
|
+
tableName: "authz_activations";
|
|
886
|
+
dataType: "string";
|
|
887
|
+
columnType: "PgText";
|
|
888
|
+
data: string;
|
|
889
|
+
driverParam: string;
|
|
890
|
+
notNull: true;
|
|
891
|
+
hasDefault: true;
|
|
892
|
+
isPrimaryKey: false;
|
|
893
|
+
isAutoincrement: false;
|
|
894
|
+
hasRuntimeDefault: false;
|
|
895
|
+
enumValues: [string, ...string[]];
|
|
896
|
+
baseColumn: never;
|
|
897
|
+
identity: undefined;
|
|
898
|
+
generated: undefined;
|
|
899
|
+
}, {}, {}>;
|
|
900
|
+
requestedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
901
|
+
name: "requested_at";
|
|
902
|
+
tableName: "authz_activations";
|
|
903
|
+
dataType: "date";
|
|
904
|
+
columnType: "PgTimestamp";
|
|
905
|
+
data: Date;
|
|
906
|
+
driverParam: string;
|
|
907
|
+
notNull: true;
|
|
908
|
+
hasDefault: true;
|
|
909
|
+
isPrimaryKey: false;
|
|
910
|
+
isAutoincrement: false;
|
|
911
|
+
hasRuntimeDefault: false;
|
|
912
|
+
enumValues: undefined;
|
|
913
|
+
baseColumn: never;
|
|
914
|
+
identity: undefined;
|
|
915
|
+
generated: undefined;
|
|
916
|
+
}, {}, {}>;
|
|
917
|
+
startsAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
918
|
+
name: "starts_at";
|
|
919
|
+
tableName: "authz_activations";
|
|
920
|
+
dataType: "date";
|
|
921
|
+
columnType: "PgTimestamp";
|
|
922
|
+
data: Date;
|
|
923
|
+
driverParam: string;
|
|
924
|
+
notNull: true;
|
|
925
|
+
hasDefault: false;
|
|
926
|
+
isPrimaryKey: false;
|
|
927
|
+
isAutoincrement: false;
|
|
928
|
+
hasRuntimeDefault: false;
|
|
929
|
+
enumValues: undefined;
|
|
930
|
+
baseColumn: never;
|
|
931
|
+
identity: undefined;
|
|
932
|
+
generated: undefined;
|
|
933
|
+
}, {}, {}>;
|
|
934
|
+
expiresAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
935
|
+
name: "expires_at";
|
|
936
|
+
tableName: "authz_activations";
|
|
937
|
+
dataType: "date";
|
|
938
|
+
columnType: "PgTimestamp";
|
|
939
|
+
data: Date;
|
|
940
|
+
driverParam: string;
|
|
941
|
+
notNull: true;
|
|
942
|
+
hasDefault: false;
|
|
943
|
+
isPrimaryKey: false;
|
|
944
|
+
isAutoincrement: false;
|
|
945
|
+
hasRuntimeDefault: false;
|
|
946
|
+
enumValues: undefined;
|
|
947
|
+
baseColumn: never;
|
|
948
|
+
identity: undefined;
|
|
949
|
+
generated: undefined;
|
|
950
|
+
}, {}, {}>;
|
|
951
|
+
roleId: import("drizzle-orm/pg-core").PgColumn<{
|
|
952
|
+
name: "role_id";
|
|
953
|
+
tableName: "authz_activations";
|
|
954
|
+
dataType: "string";
|
|
955
|
+
columnType: "PgUUID";
|
|
956
|
+
data: string;
|
|
957
|
+
driverParam: string;
|
|
958
|
+
notNull: true;
|
|
959
|
+
hasDefault: false;
|
|
960
|
+
isPrimaryKey: false;
|
|
961
|
+
isAutoincrement: false;
|
|
962
|
+
hasRuntimeDefault: false;
|
|
963
|
+
enumValues: undefined;
|
|
964
|
+
baseColumn: never;
|
|
965
|
+
identity: undefined;
|
|
966
|
+
generated: undefined;
|
|
967
|
+
}, {}, {}>;
|
|
968
|
+
endedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
969
|
+
name: "ended_at";
|
|
970
|
+
tableName: "authz_activations";
|
|
971
|
+
dataType: "date";
|
|
972
|
+
columnType: "PgTimestamp";
|
|
973
|
+
data: Date;
|
|
974
|
+
driverParam: string;
|
|
975
|
+
notNull: false;
|
|
976
|
+
hasDefault: false;
|
|
977
|
+
isPrimaryKey: false;
|
|
978
|
+
isAutoincrement: false;
|
|
979
|
+
hasRuntimeDefault: false;
|
|
980
|
+
enumValues: undefined;
|
|
981
|
+
baseColumn: never;
|
|
982
|
+
identity: undefined;
|
|
983
|
+
generated: undefined;
|
|
984
|
+
}, {}, {}>;
|
|
985
|
+
endedBy: import("drizzle-orm/pg-core").PgColumn<{
|
|
986
|
+
name: "ended_by";
|
|
987
|
+
tableName: "authz_activations";
|
|
988
|
+
dataType: "string";
|
|
989
|
+
columnType: "PgText";
|
|
990
|
+
data: string;
|
|
991
|
+
driverParam: string;
|
|
992
|
+
notNull: false;
|
|
993
|
+
hasDefault: false;
|
|
994
|
+
isPrimaryKey: false;
|
|
995
|
+
isAutoincrement: false;
|
|
996
|
+
hasRuntimeDefault: false;
|
|
997
|
+
enumValues: [string, ...string[]];
|
|
998
|
+
baseColumn: never;
|
|
999
|
+
identity: undefined;
|
|
1000
|
+
generated: undefined;
|
|
1001
|
+
}, {}, {}>;
|
|
1002
|
+
};
|
|
1003
|
+
dialect: "pg";
|
|
1004
|
+
}>;
|
|
1005
|
+
/**
|
|
1006
|
+
* One row per decision on an activation, never edited -- the two-person
|
|
1007
|
+
* approval and self-approve-after-wait counting is a host's own check, not a
|
|
1008
|
+
* DB constraint (ADR 0016).
|
|
1009
|
+
*/
|
|
1010
|
+
export declare const policyActivationApprovals: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1011
|
+
name: "authz_activation_approvals";
|
|
1012
|
+
schema: undefined;
|
|
1013
|
+
columns: {
|
|
1014
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
1015
|
+
name: "id";
|
|
1016
|
+
tableName: "authz_activation_approvals";
|
|
1017
|
+
dataType: "string";
|
|
1018
|
+
columnType: "PgUUID";
|
|
1019
|
+
data: string;
|
|
1020
|
+
driverParam: string;
|
|
1021
|
+
notNull: true;
|
|
1022
|
+
hasDefault: true;
|
|
1023
|
+
isPrimaryKey: true;
|
|
1024
|
+
isAutoincrement: false;
|
|
1025
|
+
hasRuntimeDefault: false;
|
|
1026
|
+
enumValues: undefined;
|
|
1027
|
+
baseColumn: never;
|
|
1028
|
+
identity: undefined;
|
|
1029
|
+
generated: undefined;
|
|
1030
|
+
}, {}, {}>;
|
|
1031
|
+
activationId: import("drizzle-orm/pg-core").PgColumn<{
|
|
1032
|
+
name: "activation_id";
|
|
1033
|
+
tableName: "authz_activation_approvals";
|
|
1034
|
+
dataType: "string";
|
|
1035
|
+
columnType: "PgUUID";
|
|
1036
|
+
data: string;
|
|
1037
|
+
driverParam: string;
|
|
1038
|
+
notNull: true;
|
|
1039
|
+
hasDefault: false;
|
|
1040
|
+
isPrimaryKey: false;
|
|
1041
|
+
isAutoincrement: false;
|
|
1042
|
+
hasRuntimeDefault: false;
|
|
1043
|
+
enumValues: undefined;
|
|
1044
|
+
baseColumn: never;
|
|
1045
|
+
identity: undefined;
|
|
1046
|
+
generated: undefined;
|
|
1047
|
+
}, {}, {}>;
|
|
1048
|
+
approverId: import("drizzle-orm/pg-core").PgColumn<{
|
|
1049
|
+
name: "approver_id";
|
|
1050
|
+
tableName: "authz_activation_approvals";
|
|
1051
|
+
dataType: "string";
|
|
1052
|
+
columnType: "PgText";
|
|
1053
|
+
data: string;
|
|
1054
|
+
driverParam: string;
|
|
1055
|
+
notNull: true;
|
|
1056
|
+
hasDefault: false;
|
|
1057
|
+
isPrimaryKey: false;
|
|
1058
|
+
isAutoincrement: false;
|
|
1059
|
+
hasRuntimeDefault: false;
|
|
1060
|
+
enumValues: [string, ...string[]];
|
|
1061
|
+
baseColumn: never;
|
|
1062
|
+
identity: undefined;
|
|
1063
|
+
generated: undefined;
|
|
1064
|
+
}, {}, {}>;
|
|
1065
|
+
approverClass: import("drizzle-orm/pg-core").PgColumn<{
|
|
1066
|
+
name: "approver_class";
|
|
1067
|
+
tableName: "authz_activation_approvals";
|
|
1068
|
+
dataType: "string";
|
|
1069
|
+
columnType: "PgText";
|
|
1070
|
+
data: string;
|
|
1071
|
+
driverParam: string;
|
|
1072
|
+
notNull: true;
|
|
1073
|
+
hasDefault: false;
|
|
1074
|
+
isPrimaryKey: false;
|
|
1075
|
+
isAutoincrement: false;
|
|
1076
|
+
hasRuntimeDefault: false;
|
|
1077
|
+
enumValues: [string, ...string[]];
|
|
1078
|
+
baseColumn: never;
|
|
1079
|
+
identity: undefined;
|
|
1080
|
+
generated: undefined;
|
|
1081
|
+
}, {}, {}>;
|
|
1082
|
+
decision: import("drizzle-orm/pg-core").PgColumn<{
|
|
1083
|
+
name: "decision";
|
|
1084
|
+
tableName: "authz_activation_approvals";
|
|
1085
|
+
dataType: "string";
|
|
1086
|
+
columnType: "PgText";
|
|
1087
|
+
data: string;
|
|
1088
|
+
driverParam: string;
|
|
1089
|
+
notNull: true;
|
|
1090
|
+
hasDefault: false;
|
|
1091
|
+
isPrimaryKey: false;
|
|
1092
|
+
isAutoincrement: false;
|
|
1093
|
+
hasRuntimeDefault: false;
|
|
1094
|
+
enumValues: [string, ...string[]];
|
|
1095
|
+
baseColumn: never;
|
|
1096
|
+
identity: undefined;
|
|
1097
|
+
generated: undefined;
|
|
1098
|
+
}, {}, {}>;
|
|
1099
|
+
reason: import("drizzle-orm/pg-core").PgColumn<{
|
|
1100
|
+
name: "reason";
|
|
1101
|
+
tableName: "authz_activation_approvals";
|
|
1102
|
+
dataType: "string";
|
|
1103
|
+
columnType: "PgText";
|
|
1104
|
+
data: string;
|
|
1105
|
+
driverParam: string;
|
|
1106
|
+
notNull: false;
|
|
1107
|
+
hasDefault: false;
|
|
1108
|
+
isPrimaryKey: false;
|
|
1109
|
+
isAutoincrement: false;
|
|
1110
|
+
hasRuntimeDefault: false;
|
|
1111
|
+
enumValues: [string, ...string[]];
|
|
1112
|
+
baseColumn: never;
|
|
1113
|
+
identity: undefined;
|
|
1114
|
+
generated: undefined;
|
|
1115
|
+
}, {}, {}>;
|
|
1116
|
+
decidedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
1117
|
+
name: "decided_at";
|
|
1118
|
+
tableName: "authz_activation_approvals";
|
|
1119
|
+
dataType: "date";
|
|
1120
|
+
columnType: "PgTimestamp";
|
|
1121
|
+
data: Date;
|
|
1122
|
+
driverParam: string;
|
|
1123
|
+
notNull: true;
|
|
1124
|
+
hasDefault: true;
|
|
1125
|
+
isPrimaryKey: false;
|
|
1126
|
+
isAutoincrement: false;
|
|
1127
|
+
hasRuntimeDefault: false;
|
|
1128
|
+
enumValues: undefined;
|
|
1129
|
+
baseColumn: never;
|
|
1130
|
+
identity: undefined;
|
|
1131
|
+
generated: undefined;
|
|
1132
|
+
}, {}, {}>;
|
|
1133
|
+
};
|
|
1134
|
+
dialect: "pg";
|
|
1135
|
+
}>;
|
|
687
1136
|
export declare const policyVersion: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
688
1137
|
name: "authz_policy_version";
|
|
689
1138
|
schema: undefined;
|
package/dist/policy-schema.js
CHANGED
|
@@ -46,6 +46,69 @@ export const policyAssignments = pgTable('authz_assignments', {
|
|
|
46
46
|
index('authz_assignments_role_idx').on(t.tenantId, t.roleId),
|
|
47
47
|
index('authz_assignments_team_idx').on(t.tenantId, t.teamId),
|
|
48
48
|
]);
|
|
49
|
+
/**
|
|
50
|
+
* A human principal's time-boxed request to exercise a subset of the
|
|
51
|
+
* ELEVATED permissions they are otherwise only eligible for (ADR 0016, "No
|
|
52
|
+
* standing authority"). Ported from Boule's `drizzle/0014_activations.sql`
|
|
53
|
+
* (authz#83 wave 11b, `migrations/0005_activations.sql` here);
|
|
54
|
+
* `authz_roles.updated_by` above and the `'activation'`
|
|
55
|
+
* `authz_assignments.source` value that same source file added are already
|
|
56
|
+
* in `0001_baseline.sql`, so only these two tables land in the new file.
|
|
57
|
+
*/
|
|
58
|
+
export const policyActivations = pgTable('authz_activations', {
|
|
59
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
60
|
+
tenantId: uuid('tenant_id').notNull(),
|
|
61
|
+
applicationId: text('application_id').notNull(),
|
|
62
|
+
platformId: text('platform_id').notNull(),
|
|
63
|
+
// Agents and services can never open an activation (no inbox for the
|
|
64
|
+
// self-approve-wait email, nothing eligible to approve on their behalf),
|
|
65
|
+
// so unlike memberships.principal_id/authz_assignments.principal_id this
|
|
66
|
+
// is always a person.
|
|
67
|
+
principalId: text('principal_id').notNull(),
|
|
68
|
+
principalClass: text('principal_class').notNull(),
|
|
69
|
+
// The requested subset of what the principal is eligible for. Checked
|
|
70
|
+
// against a host's policy `elevated` set in code, not here: that set is
|
|
71
|
+
// a host's own catalogue data, not the database's.
|
|
72
|
+
permissions: text('permissions').array().notNull(),
|
|
73
|
+
reason: text('reason').notNull(),
|
|
74
|
+
reference: text('reference').notNull(),
|
|
75
|
+
status: text('status').notNull().default('pending'),
|
|
76
|
+
requestedAt: timestamp('requested_at', { withTimezone: true }).notNull().defaultNow(),
|
|
77
|
+
startsAt: timestamp('starts_at', { withTimezone: true }).notNull(),
|
|
78
|
+
expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
|
|
79
|
+
// The non-primary authz_roles row minted with exactly the requested
|
|
80
|
+
// entries, the same indirection break_glass_sessions.role_id uses.
|
|
81
|
+
roleId: uuid('role_id').notNull(),
|
|
82
|
+
endedAt: timestamp('ended_at', { withTimezone: true }),
|
|
83
|
+
endedBy: text('ended_by'),
|
|
84
|
+
}, (t) => [
|
|
85
|
+
// Full definition is partial (WHERE status = 'pending') in the shipped
|
|
86
|
+
// migration; this version of drizzle's table builder cannot express
|
|
87
|
+
// that predicate, the same gap `src/testing/database.ts` documents for
|
|
88
|
+
// `bg_active_idx` and the other non-unique partial indexes this package
|
|
89
|
+
// ships.
|
|
90
|
+
index('authz_activations_pending_idx').on(t.tenantId, t.principalId),
|
|
91
|
+
index('authz_activations_tenant_idx').on(t.tenantId, t.requestedAt.desc()),
|
|
92
|
+
]);
|
|
93
|
+
/**
|
|
94
|
+
* One row per decision on an activation, never edited -- the two-person
|
|
95
|
+
* approval and self-approve-after-wait counting is a host's own check, not a
|
|
96
|
+
* DB constraint (ADR 0016).
|
|
97
|
+
*/
|
|
98
|
+
export const policyActivationApprovals = pgTable('authz_activation_approvals', {
|
|
99
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
100
|
+
activationId: uuid('activation_id').notNull(),
|
|
101
|
+
// Same owner-column reasoning as policyActivations.principalId above: an
|
|
102
|
+
// approver is always a person.
|
|
103
|
+
approverId: text('approver_id').notNull(),
|
|
104
|
+
approverClass: text('approver_class').notNull(),
|
|
105
|
+
decision: text('decision').notNull(),
|
|
106
|
+
reason: text('reason'),
|
|
107
|
+
decidedAt: timestamp('decided_at', { withTimezone: true }).notNull().defaultNow(),
|
|
108
|
+
}, (t) => [
|
|
109
|
+
index('authz_activation_approvals_activation_idx').on(t.activationId, t.decidedAt.desc()),
|
|
110
|
+
index('authz_activation_approvals_approver_idx').on(t.approverId),
|
|
111
|
+
]);
|
|
49
112
|
export const policyVersion = pgTable('authz_policy_version', {
|
|
50
113
|
id: integer('id').primaryKey(),
|
|
51
114
|
version: integer('version').notNull(),
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type ResourcePermission, type ResourceRole, type TenantKind } from '@wtfalch/authz';
|
|
2
|
+
import type { StoreBinding } from './binding.js';
|
|
3
|
+
/** One `systemRoles[kind][key]` entry of a host's policy catalogue JSON. */
|
|
4
|
+
export interface PolicyRoleTemplate {
|
|
5
|
+
readonly key: string;
|
|
6
|
+
readonly label: string;
|
|
7
|
+
readonly description: string;
|
|
8
|
+
readonly revision: number;
|
|
9
|
+
readonly guards: readonly string[];
|
|
10
|
+
readonly entries: readonly {
|
|
11
|
+
readonly permission: string;
|
|
12
|
+
readonly boundary?: string;
|
|
13
|
+
readonly scope?: unknown;
|
|
14
|
+
readonly relation?: string;
|
|
15
|
+
}[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The shape a host's own `policy-catalogue.json` must have for `definePolicy` to build a
|
|
19
|
+
* `StorePolicy` from it. The store has no catalogue of its own — this is the host's data,
|
|
20
|
+
* typed so `definePolicy` can be generic over it.
|
|
21
|
+
*/
|
|
22
|
+
export interface PolicyData {
|
|
23
|
+
readonly permissions: Record<string, ResourcePermission>;
|
|
24
|
+
readonly defaultCeiling: readonly string[];
|
|
25
|
+
readonly maxDepth: number;
|
|
26
|
+
/** Partial: a host's JSON need not define a role set for every `TenantKind` (Boule's has none for `product`); asking for a missing kind throws. */
|
|
27
|
+
readonly systemRoles: Partial<Record<TenantKind, Record<string, PolicyRoleTemplate>>>;
|
|
28
|
+
/** ADR 0016: the permissions that are eligible, not standingly usable, by default. */
|
|
29
|
+
readonly elevated: readonly string[];
|
|
30
|
+
/** The subset of `elevated` a second person must also approve before it activates. */
|
|
31
|
+
readonly coApproval: readonly string[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A `StoreBinding` plus everything a host's own `policy-catalogue.ts` used to export: the
|
|
35
|
+
* compiled catalogue, the built-in and starter role templates, and the ADR 0016 sets. Only
|
|
36
|
+
* `roles.ts` and `boot.ts` need the template methods; everywhere else a plain `StoreBinding`
|
|
37
|
+
* (which this also is) is enough.
|
|
38
|
+
*/
|
|
39
|
+
export interface StorePolicy extends StoreBinding {
|
|
40
|
+
readonly defaultCeiling: readonly string[];
|
|
41
|
+
readonly maxDepth: number;
|
|
42
|
+
/** ADR 0016: the permissions that are eligible, not standingly usable, by default. */
|
|
43
|
+
readonly elevated: ReadonlySet<string>;
|
|
44
|
+
/** The subset of `elevated` a second person must also approve before it activates. */
|
|
45
|
+
readonly requiresCoApproval: ReadonlySet<string>;
|
|
46
|
+
readonly offered: readonly string[];
|
|
47
|
+
/**
|
|
48
|
+
* ADR 0016: the only true built-in left is `self_service` — resynced on every boot. Every
|
|
49
|
+
* other `systemRoles` entry (`owner`, `admin`, `billing`, `member`) is a starter role, seeded
|
|
50
|
+
* once by `starterRoles` below, as an ordinary editable row nothing here resyncs again.
|
|
51
|
+
*/
|
|
52
|
+
builtInRoles(organisationId: string, kind: TenantKind): Omit<ResourceRole, 'id'>[];
|
|
53
|
+
/** The starter role set a new tenant of this kind is seeded with once, at creation (ADR 0016). */
|
|
54
|
+
starterRoles(organisationId: string, kind: TenantKind): Omit<ResourceRole, 'id'>[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Builds a `StorePolicy` from a host's own `policy-catalogue.json` data and its
|
|
58
|
+
* `{ applicationId, platformId }`. Generic over the host's data because every host's catalogue
|
|
59
|
+
* differs; this function is the one piece of that shape both applications wrote identically.
|
|
60
|
+
*/
|
|
61
|
+
export declare function definePolicy(data: PolicyData, binding: {
|
|
62
|
+
readonly applicationId: string;
|
|
63
|
+
readonly platformId: string;
|
|
64
|
+
}): StorePolicy;
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { defineResourceCatalogue, } from '@wtfalch/authz';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a `StorePolicy` from a host's own `policy-catalogue.json` data and its
|
|
4
|
+
* `{ applicationId, platformId }`. Generic over the host's data because every host's catalogue
|
|
5
|
+
* differs; this function is the one piece of that shape both applications wrote identically.
|
|
6
|
+
*/
|
|
7
|
+
export function definePolicy(data, binding) {
|
|
8
|
+
const namespaces = [
|
|
9
|
+
...new Set(Object.keys(data.permissions).map((key) => key.split(/[.:]/)[0])),
|
|
10
|
+
];
|
|
11
|
+
const catalogue = defineResourceCatalogue(namespaces.map((namespace) => ({
|
|
12
|
+
namespace,
|
|
13
|
+
permissions: Object.values(data.permissions).filter((p) => p.id.split(/[.:]/)[0] === namespace),
|
|
14
|
+
})));
|
|
15
|
+
// A tenant kind this host has no roles for is a configuration error. Seeding it
|
|
16
|
+
// with nothing would leave the tenant without an owner seat, so fail loudly,
|
|
17
|
+
// as the hosts' own catalogue did.
|
|
18
|
+
const rolesFor = (kind) => {
|
|
19
|
+
const roles = data.systemRoles[kind];
|
|
20
|
+
if (!roles)
|
|
21
|
+
throw new Error(`the policy catalogue has no system roles for tenant kind "${kind}"`);
|
|
22
|
+
return roles;
|
|
23
|
+
};
|
|
24
|
+
function roleFrom(organisationId, role, builtIn) {
|
|
25
|
+
return {
|
|
26
|
+
key: role.key,
|
|
27
|
+
applicationId: binding.applicationId,
|
|
28
|
+
platformId: binding.platformId,
|
|
29
|
+
organisationId,
|
|
30
|
+
label: role.label,
|
|
31
|
+
description: role.description,
|
|
32
|
+
revision: role.revision,
|
|
33
|
+
builtIn,
|
|
34
|
+
guards: [...role.guards],
|
|
35
|
+
entries: role.entries.map((entry) => ({
|
|
36
|
+
permission: entry.permission,
|
|
37
|
+
boundary: (entry.boundary === 'platform'
|
|
38
|
+
? { kind: 'platform' }
|
|
39
|
+
: { kind: 'organisation', organisationId }),
|
|
40
|
+
scope: ('scope' in entry ? entry.scope : { kind: 'organisation' }),
|
|
41
|
+
relation: ('relation' in entry ? entry.relation : 'any'),
|
|
42
|
+
})),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
applicationId: binding.applicationId,
|
|
47
|
+
platformId: binding.platformId,
|
|
48
|
+
catalogue,
|
|
49
|
+
defaultCeiling: data.defaultCeiling,
|
|
50
|
+
maxDepth: data.maxDepth,
|
|
51
|
+
elevated: new Set(data.elevated),
|
|
52
|
+
requiresCoApproval: new Set(data.coApproval),
|
|
53
|
+
offered: Object.keys(catalogue).filter((key) => catalogue[key]?.offered),
|
|
54
|
+
builtInRoles(organisationId, kind) {
|
|
55
|
+
return Object.values(rolesFor(kind))
|
|
56
|
+
.filter((role) => role.key === 'self_service')
|
|
57
|
+
.map((role) => roleFrom(organisationId, role, true));
|
|
58
|
+
},
|
|
59
|
+
starterRoles(organisationId, kind) {
|
|
60
|
+
return Object.values(rolesFor(kind))
|
|
61
|
+
.filter((role) => role.key !== 'self_service')
|
|
62
|
+
.map((role) => roleFrom(organisationId, role, false));
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|