@uptime.app/contracts 3.0.51 → 3.0.52-rc.0.sso
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/cjs/b2b-organisation/b2b-organisation-group.d.ts +0 -1
- package/dist/cjs/b2b-organisation/b2b-organisation-group.d.ts.map +1 -1
- package/dist/cjs/b2b-organisation/b2b-organisation-group.js +1 -23
- package/dist/cjs/b2b-organisation/b2b-organisation-group.js.map +1 -1
- package/dist/cjs/identity-provider/identity-provider.d.ts +4 -0
- package/dist/cjs/identity-provider/identity-provider.d.ts.map +1 -1
- package/dist/esm/b2b-organisation/b2b-organisation-group.d.ts +0 -1
- package/dist/esm/b2b-organisation/b2b-organisation-group.d.ts.map +1 -1
- package/dist/esm/b2b-organisation/b2b-organisation-group.js +0 -22
- package/dist/esm/b2b-organisation/b2b-organisation-group.js.map +1 -1
- package/dist/esm/identity-provider/identity-provider.d.ts +4 -0
- package/dist/esm/identity-provider/identity-provider.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/access-control/collection-access-control.ts +28 -28
- package/src/access-control/hack-access-control.ts +32 -32
- package/src/access-control/profile-access-control.ts +27 -27
- package/src/app-hack/app-full-hack.ts +9 -9
- package/src/app-hack/app-hack-metadata.ts +61 -61
- package/src/app-hack/app-restricted-hack.ts +22 -22
- package/src/b2b-content-access/b2b-content-access.ts +57 -57
- package/src/b2b-organisation/b2b-carousel.ts +33 -33
- package/src/b2b-organisation/b2b-org-idp-mapping.ts +27 -27
- package/src/b2b-organisation/b2b-organisation-events.ts +21 -21
- package/src/b2b-organisation/b2b-organisation-group-events.ts +21 -21
- package/src/b2b-organisation/b2b-organisation-group-membership-events.ts +21 -21
- package/src/b2b-organisation/b2b-organisation-group-membership.ts +17 -17
- package/src/b2b-organisation/b2b-organisation-group.ts +43 -66
- package/src/b2b-organisation/b2b-organisation-invitation.ts +45 -45
- package/src/b2b-organisation/b2b-organisation-member-events.ts +21 -21
- package/src/b2b-organisation/b2b-organisation-member.ts +19 -19
- package/src/b2b-organisation/b2b-organisation.ts +38 -38
- package/src/brand/brand-events.ts +21 -21
- package/src/brand/brand.ts +77 -77
- package/src/catalogue/catalogue.ts +152 -152
- package/src/cms-hack/cms-searchable-hack.ts +44 -44
- package/src/collection/collection-events.ts +21 -21
- package/src/collection/collection.ts +59 -59
- package/src/collection/enriched-collection-events.ts +18 -18
- package/src/collection/enriched-collection.ts +65 -65
- package/src/course/course-events.ts +20 -20
- package/src/course/course.ts +25 -25
- package/src/course-enrollment/course-enrollment-events.ts +21 -21
- package/src/course-enrollment/course-enrollment.ts +36 -36
- package/src/creator/creator-events.ts +32 -32
- package/src/creator/creator.ts +7 -7
- package/src/distribution/collection-distribution.ts +17 -17
- package/src/distribution/distribution-events.ts +61 -61
- package/src/distribution/hack-distribution.ts +18 -18
- package/src/draft-hack/draft-hack-events.ts +21 -21
- package/src/draft-hack/draft-hack.ts +190 -190
- package/src/draft-hack-generation/draft-hack-generation-commands.ts +10 -10
- package/src/draft-hack-generation/draft-hack-generation-events.ts +52 -52
- package/src/draft-hack-generation/draft-hack-generation.ts +39 -39
- package/src/enriched-b2b-organisation/enriched-b2b-organisation-events.ts +21 -21
- package/src/enriched-b2b-organisation/enriched-b2b-organisation.ts +37 -37
- package/src/enriched-hack/enriched-hack-events.ts +21 -21
- package/src/enriched-hack/enriched-hack.ts +109 -109
- package/src/enrichment-asset/hack-enrichment-video-events.ts +55 -55
- package/src/enrichment-asset/hack-enrichment-video.ts +40 -40
- package/src/entitlement/user-entitlements-commands.ts +54 -54
- package/src/entitlement/user-entitlements-events.ts +29 -29
- package/src/entitlement/user-entitlements.ts +18 -18
- package/src/flashcard/flashcard-events.ts +85 -85
- package/src/flashcard/flashcard.ts +138 -138
- package/src/hack/extracted-subject.ts +31 -31
- package/src/hack/hack-events.ts +21 -21
- package/src/hack/hack-published.ts +21 -21
- package/src/hack/hack-with-voice-events.ts +20 -20
- package/src/hack/hack-with-voice.ts +36 -36
- package/src/hack/hack.ts +127 -127
- package/src/hack/publish-hack-command.ts +20 -20
- package/src/hack/republish-hack-command.ts +20 -20
- package/src/hack-mapping/access-helper.ts +6 -6
- package/src/hack-mapping/app-full-hack.test.ts +13 -13
- package/src/hack-mapping/app-full-hack.ts +14 -14
- package/src/hack-mapping/app-hack-metadata.test.ts +22 -22
- package/src/hack-mapping/app-hack-metadata.ts +82 -82
- package/src/hack-mapping/app-restricted-hack.test.ts +12 -12
- package/src/hack-mapping/app-restricted-hack.ts +21 -21
- package/src/hack-mapping/remove-undefined-fields.ts +9 -9
- package/src/hack-mapping/test-data/enriched-hack.json +2576 -2576
- package/src/hack-mapping/test-data/expected-app-full-hack.json +2509 -2509
- package/src/hack-mapping/test-data/expected-app-hack-metadata.json +93 -93
- package/src/hack-mapping/test-data/expected-app-restricted-hack.json +799 -799
- package/src/hack-metadata/hack-metadata-events.ts +21 -21
- package/src/hack-metadata/hack-metadata.ts +17 -17
- package/src/hack-of-the-day/hack-of-the-day-events.ts +21 -21
- package/src/hack-of-the-day/hack-of-the-day.ts +13 -13
- package/src/hack-statistics/hack-statistics-events.ts +21 -21
- package/src/hack-statistics/hack-statistics.ts +9 -9
- package/src/hackbot/hack-generation-commands.ts +45 -45
- package/src/hackbot/hack-generation-trigger-events.ts +21 -21
- package/src/hackbot/hack-generation-trigger.ts +57 -57
- package/src/history/history-events.ts +21 -21
- package/src/history/history.ts +46 -46
- package/src/identity-provider/domain-identity-provider-events.ts +20 -20
- package/src/identity-provider/domain-identity-provider.ts +10 -10
- package/src/identity-provider/identity-provider-events.ts +20 -20
- package/src/identity-provider/identity-provider.ts +51 -47
- package/src/index.ts +220 -220
- package/src/interest/interest-events.ts +21 -21
- package/src/interest/interest.ts +15 -15
- package/src/knowledge-source/knowledge-source-events.ts +21 -21
- package/src/knowledge-source/knowledge-source.ts +43 -43
- package/src/knowledge-source-parsing/knowledge-source-metadata.ts +58 -58
- package/src/knowledge-source-parsing/knowledge-source-parsing-commands.ts +41 -41
- package/src/knowledge-source-parsing/knowledge-source-parsing-events.ts +30 -30
- package/src/knowledge-source-parsing/podcast-metadata.ts +44 -44
- package/src/knowledge-source-parsing/podcast-parsing-events.ts +28 -28
- package/src/knowledge-source-parsing/user-hack-generation-options.ts +7 -7
- package/src/learn-upon/get-sqsso-url.ts +13 -13
- package/src/learn-upon/learn-upon-commands.ts +33 -33
- package/src/learn-upon/learn-upon-course-enrollment-events.ts +21 -21
- package/src/learn-upon/learn-upon-course-enrollment.ts +62 -62
- package/src/learn-upon/learn-upon-course-events.ts +21 -21
- package/src/learn-upon/learn-upon-course.ts +108 -108
- package/src/learn-upon/learn-upon-portal-events.ts +20 -20
- package/src/learn-upon/learn-upon-portal.ts +12 -12
- package/src/learn-upon/learn-upon-user-mapping-events.ts +18 -18
- package/src/learn-upon/learn-upon-user-mapping.ts +9 -9
- package/src/learn-upon/learn-upon-webhook-events.ts +80 -80
- package/src/learn-upon/learn-upon-webhook-types.ts +346 -346
- package/src/learn-upon/unenroll.ts +6 -6
- package/src/localisation/live-locales.ts +45 -45
- package/src/localisation/locale-language.ts +4 -4
- package/src/localisation/supported-languages.ts +11 -11
- package/src/localisation/supported-locales.ts +67 -67
- package/src/message-bus/context.ts +7 -7
- package/src/moderation/hack-moderation-result-events.ts +18 -18
- package/src/moderation/hack-moderation-result.ts +27 -27
- package/src/partner/partner-events.ts +21 -21
- package/src/partner/partner.ts +7 -7
- package/src/pending-hack/pending-hack-events.ts +20 -20
- package/src/pending-hack/pending-hack-with-voice-events.ts +20 -20
- package/src/pending-hack/pending-hack-with-voice.ts +25 -25
- package/src/pending-hack/pending-hack.ts +78 -78
- package/src/preferences/preferences-events.ts +15 -15
- package/src/preferences/preferences.ts +35 -35
- package/src/profile/profile-events.ts +41 -41
- package/src/profile/profile.ts +36 -36
- package/src/profile/translated-profile.ts +13 -13
- package/src/referral/referral-events.ts +41 -41
- package/src/referral/referral.ts +76 -76
- package/src/saved/saved-collection-events.ts +19 -19
- package/src/saved/saved-collection.ts +8 -8
- package/src/segment/segment-commands.ts +75 -75
- package/src/segment/segment-events.ts +42 -42
- package/src/segment/segment-with-voice.ts +8 -8
- package/src/segment/segment.ts +82 -82
- package/src/service-simulator/mock-request-response.ts +17 -17
- package/src/skin/skin-events.ts +21 -21
- package/src/skin/skin.ts +6 -6
- package/src/spark/spark-board-read-model.ts +17 -17
- package/src/spark/spark-board.ts +12 -12
- package/src/spark/spark-events.ts +42 -42
- package/src/spark/spark.ts +75 -75
- package/src/text-analysis/hack-categories-extracted-events.ts +24 -24
- package/src/text-analysis/hack-categories-extracted.ts +31 -31
- package/src/text-analysis/hack-entities-extracted-events.ts +24 -24
- package/src/text-analysis/hack-entities-extracted.ts +38 -38
- package/src/text-analysis/hack-keywords-extracted-events.ts +24 -24
- package/src/text-analysis/hack-keywords-extracted.ts +23 -23
- package/src/text-analysis/hack-topics-extracted-events.ts +24 -24
- package/src/text-analysis/hack-topics-extracted.ts +31 -31
- package/src/text-to-speech/create-speech-commands.ts +22 -22
- package/src/text-to-speech/speech-created-events.ts +10 -10
- package/src/text-to-speech/supported-voices.ts +222 -222
- package/src/text-to-speech/text-segment.ts +29 -29
- package/src/theme/theme-events.ts +21 -21
- package/src/theme/theme.ts +68 -68
- package/src/translation/translation-events.ts +73 -73
- package/src/translation/translation.ts +25 -25
- package/src/trending/trending-events.ts +21 -21
- package/src/trending/trending-hacks.ts +8 -8
- package/src/user/cognito-user-authentication-log.ts +7 -7
- package/src/user/get-user.ts +63 -63
- package/src/user/user-events.ts +39 -39
- package/src/user-acl/user-acl-utils.test.ts +273 -273
- package/src/user-acl/user-acl-utils.ts +176 -176
- package/src/user-context/user-context-events.ts +21 -21
- package/src/user-context/user-context.ts +15 -15
- package/src/user-notification-context/user-notification-context-events.ts +21 -21
- package/src/user-notification-context/user-notification-context.ts +9 -9
- package/src/user-statistics/user-statistics-events.ts +97 -97
- package/src/user-statistics/user-statistics.ts +82 -82
- package/src/video-cropping/video-cropping-events.ts +43 -43
- package/src/video-cropping/video-metadata.ts +12 -12
- package/tsconfig.cjs.json +7 -7
- package/tsconfig.npm.json +22 -22
|
@@ -1,273 +1,273 @@
|
|
|
1
|
-
import { parseAclEntry, getAclValuesByNamespace, aclFor, UserAclBuilder } from './user-acl-utils';
|
|
2
|
-
|
|
3
|
-
describe('parseAclEntry', () => {
|
|
4
|
-
|
|
5
|
-
test('returns null for empty string', () => {
|
|
6
|
-
expect(parseAclEntry('')).toBeNull();
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
test('returns null for whitespace-only string', () => {
|
|
10
|
-
expect(parseAclEntry(' ')).toBeNull();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test('parses a simple namespace-only entry', () => {
|
|
14
|
-
expect(parseAclEntry('public')).toEqual({
|
|
15
|
-
raw: 'public',
|
|
16
|
-
excluded: false,
|
|
17
|
-
namespace: 'public',
|
|
18
|
-
value: null
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test('parses a namespace:value entry', () => {
|
|
23
|
-
expect(parseAclEntry('catalogue:uptime')).toEqual({
|
|
24
|
-
raw: 'catalogue:uptime',
|
|
25
|
-
excluded: false,
|
|
26
|
-
namespace: 'catalogue',
|
|
27
|
-
value: 'uptime'
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test('parses an excluded namespace-only entry', () => {
|
|
32
|
-
expect(parseAclEntry('!public')).toEqual({
|
|
33
|
-
raw: '!public',
|
|
34
|
-
excluded: true,
|
|
35
|
-
namespace: 'public',
|
|
36
|
-
value: null
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('parses an excluded namespace:value entry', () => {
|
|
41
|
-
expect(parseAclEntry('!catalogue:uptime')).toEqual({
|
|
42
|
-
raw: '!catalogue:uptime',
|
|
43
|
-
excluded: true,
|
|
44
|
-
namespace: 'catalogue',
|
|
45
|
-
value: 'uptime'
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test('trims leading and trailing whitespace', () => {
|
|
50
|
-
expect(parseAclEntry(' catalogue:uptime ')).toEqual({
|
|
51
|
-
raw: 'catalogue:uptime',
|
|
52
|
-
excluded: false,
|
|
53
|
-
namespace: 'catalogue',
|
|
54
|
-
value: 'uptime'
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('returns null when namespace is empty (entry starts with colon)', () => {
|
|
59
|
-
expect(parseAclEntry(':value')).toBeNull();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test('preserves colons in value', () => {
|
|
63
|
-
expect(parseAclEntry('catalogue:b2b:org-123')).toEqual({
|
|
64
|
-
raw: 'catalogue:b2b:org-123',
|
|
65
|
-
excluded: false,
|
|
66
|
-
namespace: 'catalogue',
|
|
67
|
-
value: 'b2b:org-123'
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe('getAclValuesByNamespace', () => {
|
|
73
|
-
const acl = [
|
|
74
|
-
'public',
|
|
75
|
-
'catalogue:uptime',
|
|
76
|
-
'catalogue:premium',
|
|
77
|
-
'!catalogue:restricted',
|
|
78
|
-
'b2b:org-1',
|
|
79
|
-
'!b2b:org-2',
|
|
80
|
-
'user:user-123'
|
|
81
|
-
];
|
|
82
|
-
|
|
83
|
-
test('returns included values for a namespace by default', () => {
|
|
84
|
-
expect(getAclValuesByNamespace(acl, 'catalogue')).toEqual(['uptime', 'premium']);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('returns excluded values when mode is "excluded"', () => {
|
|
88
|
-
expect(getAclValuesByNamespace(acl, 'catalogue', { mode: 'excluded' })).toEqual(['restricted']);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test('returns all values when mode is "all"', () => {
|
|
92
|
-
expect(getAclValuesByNamespace(acl, 'catalogue', { mode: 'all' })).toEqual(['uptime', 'premium', 'restricted']);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('returns empty array when namespace has no matches', () => {
|
|
96
|
-
expect(getAclValuesByNamespace(acl, 'subject')).toEqual([]);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test('deduplicates values by default', () => {
|
|
100
|
-
const aclWithDupes = ['catalogue:uptime', 'catalogue:uptime', 'catalogue:premium'];
|
|
101
|
-
expect(getAclValuesByNamespace(aclWithDupes, 'catalogue')).toEqual(['uptime', 'premium']);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test('preserves duplicates when dedupe is false', () => {
|
|
105
|
-
const aclWithDupes = ['catalogue:uptime', 'catalogue:uptime', 'catalogue:premium'];
|
|
106
|
-
expect(getAclValuesByNamespace(aclWithDupes, 'catalogue', { dedupe: false })).toEqual(['uptime', 'uptime', 'premium']);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test('excludes namespace-only entries (no value)', () => {
|
|
110
|
-
expect(getAclValuesByNamespace(acl, 'public')).toEqual([]);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test('returns empty array for empty acl', () => {
|
|
114
|
-
expect(getAclValuesByNamespace([], 'catalogue')).toEqual([]);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test('skips invalid entries', () => {
|
|
118
|
-
const aclWithInvalid = ['', ' ', 'catalogue:uptime'];
|
|
119
|
-
expect(getAclValuesByNamespace(aclWithInvalid, 'catalogue')).toEqual(['uptime']);
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
describe('UserAclBuilder', () => {
|
|
124
|
-
|
|
125
|
-
test('does not include "public" by default', () => {
|
|
126
|
-
const acl = new UserAclBuilder().build();
|
|
127
|
-
expect(acl).not.toContain('public');
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test('includes "public" when allowPublic is called', () => {
|
|
131
|
-
const acl = new UserAclBuilder().allowPublic().build();
|
|
132
|
-
expect(acl).toContain('public');
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test('includes user entry when allowUser is called', () => {
|
|
136
|
-
const acl = new UserAclBuilder().allowUser('user-123').build();
|
|
137
|
-
expect(acl).toContain('user:user-123');
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test('merges baseAcl entries', () => {
|
|
141
|
-
const acl = new UserAclBuilder({ baseAcl: ['catalogue:uptime', '!catalogue:restricted'] }).build();
|
|
142
|
-
expect(acl).toContain('catalogue:uptime');
|
|
143
|
-
expect(acl).toContain('!catalogue:restricted');
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
describe('allow', () => {
|
|
147
|
-
test('adds namespace:value entry', () => {
|
|
148
|
-
const acl = new UserAclBuilder().allow('catalogue', 'uptime').build();
|
|
149
|
-
expect(acl).toContain('catalogue:uptime');
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
test('adds namespace-only entry when no value provided', () => {
|
|
153
|
-
const acl = new UserAclBuilder().allow('public').build();
|
|
154
|
-
expect(acl).toContain('public');
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test('removes entry from excluded when allowed', () => {
|
|
158
|
-
const acl = new UserAclBuilder()
|
|
159
|
-
.deny('catalogue', 'uptime')
|
|
160
|
-
.allow('catalogue', 'uptime')
|
|
161
|
-
.build();
|
|
162
|
-
expect(acl).toContain('catalogue:uptime');
|
|
163
|
-
expect(acl).not.toContain('!catalogue:uptime');
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
describe('deny', () => {
|
|
168
|
-
test('adds excluded namespace:value entry', () => {
|
|
169
|
-
const acl = new UserAclBuilder().deny('catalogue', 'restricted').build();
|
|
170
|
-
expect(acl).toContain('!catalogue:restricted');
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test('removes entry from included when denied', () => {
|
|
174
|
-
const acl = new UserAclBuilder()
|
|
175
|
-
.allow('catalogue', 'uptime')
|
|
176
|
-
.deny('catalogue', 'uptime')
|
|
177
|
-
.build();
|
|
178
|
-
expect(acl).not.toContain('catalogue:uptime');
|
|
179
|
-
expect(acl).toContain('!catalogue:uptime');
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
describe('allowCatalogue', () => {
|
|
184
|
-
test('adds catalogue:<id> entry', () => {
|
|
185
|
-
const acl = new UserAclBuilder().allowCatalogue('premium').build();
|
|
186
|
-
expect(acl).toContain('catalogue:premium');
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
describe('denyCatalogue', () => {
|
|
191
|
-
test('adds !catalogue:<id> entry', () => {
|
|
192
|
-
const acl = new UserAclBuilder().denyCatalogue('restricted').build();
|
|
193
|
-
expect(acl).toContain('!catalogue:restricted');
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
describe('allowOrganisation', () => {
|
|
198
|
-
test('adds b2b:<id> entry', () => {
|
|
199
|
-
const acl = new UserAclBuilder().allowOrganisation('org-1').build();
|
|
200
|
-
expect(acl).toContain('b2b:org-1');
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
describe('denySubject', () => {
|
|
205
|
-
test('adds !subject:<value> entry', () => {
|
|
206
|
-
const acl = new UserAclBuilder().denySubject('some-topic').build();
|
|
207
|
-
expect(acl).toContain('!subject:some-topic');
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
describe('addRaw', () => {
|
|
212
|
-
test('adds an included raw entry', () => {
|
|
213
|
-
const acl = new UserAclBuilder().addRaw('catalogue:uptime').build();
|
|
214
|
-
expect(acl).toContain('catalogue:uptime');
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
test('adds an excluded raw entry', () => {
|
|
218
|
-
const acl = new UserAclBuilder().addRaw('!catalogue:restricted').build();
|
|
219
|
-
expect(acl).toContain('!catalogue:restricted');
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
test('ignores invalid raw entries', () => {
|
|
223
|
-
const acl = new UserAclBuilder().addRaw('').build();
|
|
224
|
-
expect(acl).toEqual([]);
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
describe('addMany', () => {
|
|
229
|
-
test('adds multiple entries at once', () => {
|
|
230
|
-
const acl = new UserAclBuilder()
|
|
231
|
-
.addMany(['catalogue:uptime', 'b2b:org-1', '!catalogue:restricted'])
|
|
232
|
-
.build();
|
|
233
|
-
expect(acl).toContain('catalogue:uptime');
|
|
234
|
-
expect(acl).toContain('b2b:org-1');
|
|
235
|
-
expect(acl).toContain('!catalogue:restricted');
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
describe('aclFor', () => {
|
|
241
|
-
test('includes user entry', () => {
|
|
242
|
-
const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: [] });
|
|
243
|
-
expect(acl).toContain('user:user-abc');
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
test('always includes catalogue:uptime', () => {
|
|
247
|
-
const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: [] });
|
|
248
|
-
expect(acl).toContain('catalogue:uptime');
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
test('includes provided catalogue ids', () => {
|
|
252
|
-
const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: ['premium', 'free'] });
|
|
253
|
-
expect(acl).toContain('catalogue:premium');
|
|
254
|
-
expect(acl).toContain('catalogue:free');
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
test('includes b2b catalogue entry for each organisation', () => {
|
|
258
|
-
const acl = aclFor({ userId: 'user-abc', organisationIds: ['org-1', 'org-2'], catalogueIds: [] });
|
|
259
|
-
expect(acl).toContain('catalogue:b2b:org-1');
|
|
260
|
-
expect(acl).toContain('catalogue:b2b:org-2');
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
test('includes b2b namespace entry for each organisation', () => {
|
|
264
|
-
const acl = aclFor({ userId: 'user-abc', organisationIds: ['org-1', 'org-2'], catalogueIds: [] });
|
|
265
|
-
expect(acl).toContain('b2b:org-1');
|
|
266
|
-
expect(acl).toContain('b2b:org-2');
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
test('includes public by default', () => {
|
|
270
|
-
const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: [] });
|
|
271
|
-
expect(acl).toContain('public');
|
|
272
|
-
});
|
|
273
|
-
});
|
|
1
|
+
import { parseAclEntry, getAclValuesByNamespace, aclFor, UserAclBuilder } from './user-acl-utils';
|
|
2
|
+
|
|
3
|
+
describe('parseAclEntry', () => {
|
|
4
|
+
|
|
5
|
+
test('returns null for empty string', () => {
|
|
6
|
+
expect(parseAclEntry('')).toBeNull();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('returns null for whitespace-only string', () => {
|
|
10
|
+
expect(parseAclEntry(' ')).toBeNull();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('parses a simple namespace-only entry', () => {
|
|
14
|
+
expect(parseAclEntry('public')).toEqual({
|
|
15
|
+
raw: 'public',
|
|
16
|
+
excluded: false,
|
|
17
|
+
namespace: 'public',
|
|
18
|
+
value: null
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('parses a namespace:value entry', () => {
|
|
23
|
+
expect(parseAclEntry('catalogue:uptime')).toEqual({
|
|
24
|
+
raw: 'catalogue:uptime',
|
|
25
|
+
excluded: false,
|
|
26
|
+
namespace: 'catalogue',
|
|
27
|
+
value: 'uptime'
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('parses an excluded namespace-only entry', () => {
|
|
32
|
+
expect(parseAclEntry('!public')).toEqual({
|
|
33
|
+
raw: '!public',
|
|
34
|
+
excluded: true,
|
|
35
|
+
namespace: 'public',
|
|
36
|
+
value: null
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('parses an excluded namespace:value entry', () => {
|
|
41
|
+
expect(parseAclEntry('!catalogue:uptime')).toEqual({
|
|
42
|
+
raw: '!catalogue:uptime',
|
|
43
|
+
excluded: true,
|
|
44
|
+
namespace: 'catalogue',
|
|
45
|
+
value: 'uptime'
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('trims leading and trailing whitespace', () => {
|
|
50
|
+
expect(parseAclEntry(' catalogue:uptime ')).toEqual({
|
|
51
|
+
raw: 'catalogue:uptime',
|
|
52
|
+
excluded: false,
|
|
53
|
+
namespace: 'catalogue',
|
|
54
|
+
value: 'uptime'
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('returns null when namespace is empty (entry starts with colon)', () => {
|
|
59
|
+
expect(parseAclEntry(':value')).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('preserves colons in value', () => {
|
|
63
|
+
expect(parseAclEntry('catalogue:b2b:org-123')).toEqual({
|
|
64
|
+
raw: 'catalogue:b2b:org-123',
|
|
65
|
+
excluded: false,
|
|
66
|
+
namespace: 'catalogue',
|
|
67
|
+
value: 'b2b:org-123'
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('getAclValuesByNamespace', () => {
|
|
73
|
+
const acl = [
|
|
74
|
+
'public',
|
|
75
|
+
'catalogue:uptime',
|
|
76
|
+
'catalogue:premium',
|
|
77
|
+
'!catalogue:restricted',
|
|
78
|
+
'b2b:org-1',
|
|
79
|
+
'!b2b:org-2',
|
|
80
|
+
'user:user-123'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
test('returns included values for a namespace by default', () => {
|
|
84
|
+
expect(getAclValuesByNamespace(acl, 'catalogue')).toEqual(['uptime', 'premium']);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('returns excluded values when mode is "excluded"', () => {
|
|
88
|
+
expect(getAclValuesByNamespace(acl, 'catalogue', { mode: 'excluded' })).toEqual(['restricted']);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('returns all values when mode is "all"', () => {
|
|
92
|
+
expect(getAclValuesByNamespace(acl, 'catalogue', { mode: 'all' })).toEqual(['uptime', 'premium', 'restricted']);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('returns empty array when namespace has no matches', () => {
|
|
96
|
+
expect(getAclValuesByNamespace(acl, 'subject')).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('deduplicates values by default', () => {
|
|
100
|
+
const aclWithDupes = ['catalogue:uptime', 'catalogue:uptime', 'catalogue:premium'];
|
|
101
|
+
expect(getAclValuesByNamespace(aclWithDupes, 'catalogue')).toEqual(['uptime', 'premium']);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('preserves duplicates when dedupe is false', () => {
|
|
105
|
+
const aclWithDupes = ['catalogue:uptime', 'catalogue:uptime', 'catalogue:premium'];
|
|
106
|
+
expect(getAclValuesByNamespace(aclWithDupes, 'catalogue', { dedupe: false })).toEqual(['uptime', 'uptime', 'premium']);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('excludes namespace-only entries (no value)', () => {
|
|
110
|
+
expect(getAclValuesByNamespace(acl, 'public')).toEqual([]);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('returns empty array for empty acl', () => {
|
|
114
|
+
expect(getAclValuesByNamespace([], 'catalogue')).toEqual([]);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('skips invalid entries', () => {
|
|
118
|
+
const aclWithInvalid = ['', ' ', 'catalogue:uptime'];
|
|
119
|
+
expect(getAclValuesByNamespace(aclWithInvalid, 'catalogue')).toEqual(['uptime']);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe('UserAclBuilder', () => {
|
|
124
|
+
|
|
125
|
+
test('does not include "public" by default', () => {
|
|
126
|
+
const acl = new UserAclBuilder().build();
|
|
127
|
+
expect(acl).not.toContain('public');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('includes "public" when allowPublic is called', () => {
|
|
131
|
+
const acl = new UserAclBuilder().allowPublic().build();
|
|
132
|
+
expect(acl).toContain('public');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('includes user entry when allowUser is called', () => {
|
|
136
|
+
const acl = new UserAclBuilder().allowUser('user-123').build();
|
|
137
|
+
expect(acl).toContain('user:user-123');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('merges baseAcl entries', () => {
|
|
141
|
+
const acl = new UserAclBuilder({ baseAcl: ['catalogue:uptime', '!catalogue:restricted'] }).build();
|
|
142
|
+
expect(acl).toContain('catalogue:uptime');
|
|
143
|
+
expect(acl).toContain('!catalogue:restricted');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('allow', () => {
|
|
147
|
+
test('adds namespace:value entry', () => {
|
|
148
|
+
const acl = new UserAclBuilder().allow('catalogue', 'uptime').build();
|
|
149
|
+
expect(acl).toContain('catalogue:uptime');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('adds namespace-only entry when no value provided', () => {
|
|
153
|
+
const acl = new UserAclBuilder().allow('public').build();
|
|
154
|
+
expect(acl).toContain('public');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('removes entry from excluded when allowed', () => {
|
|
158
|
+
const acl = new UserAclBuilder()
|
|
159
|
+
.deny('catalogue', 'uptime')
|
|
160
|
+
.allow('catalogue', 'uptime')
|
|
161
|
+
.build();
|
|
162
|
+
expect(acl).toContain('catalogue:uptime');
|
|
163
|
+
expect(acl).not.toContain('!catalogue:uptime');
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe('deny', () => {
|
|
168
|
+
test('adds excluded namespace:value entry', () => {
|
|
169
|
+
const acl = new UserAclBuilder().deny('catalogue', 'restricted').build();
|
|
170
|
+
expect(acl).toContain('!catalogue:restricted');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('removes entry from included when denied', () => {
|
|
174
|
+
const acl = new UserAclBuilder()
|
|
175
|
+
.allow('catalogue', 'uptime')
|
|
176
|
+
.deny('catalogue', 'uptime')
|
|
177
|
+
.build();
|
|
178
|
+
expect(acl).not.toContain('catalogue:uptime');
|
|
179
|
+
expect(acl).toContain('!catalogue:uptime');
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe('allowCatalogue', () => {
|
|
184
|
+
test('adds catalogue:<id> entry', () => {
|
|
185
|
+
const acl = new UserAclBuilder().allowCatalogue('premium').build();
|
|
186
|
+
expect(acl).toContain('catalogue:premium');
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('denyCatalogue', () => {
|
|
191
|
+
test('adds !catalogue:<id> entry', () => {
|
|
192
|
+
const acl = new UserAclBuilder().denyCatalogue('restricted').build();
|
|
193
|
+
expect(acl).toContain('!catalogue:restricted');
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('allowOrganisation', () => {
|
|
198
|
+
test('adds b2b:<id> entry', () => {
|
|
199
|
+
const acl = new UserAclBuilder().allowOrganisation('org-1').build();
|
|
200
|
+
expect(acl).toContain('b2b:org-1');
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe('denySubject', () => {
|
|
205
|
+
test('adds !subject:<value> entry', () => {
|
|
206
|
+
const acl = new UserAclBuilder().denySubject('some-topic').build();
|
|
207
|
+
expect(acl).toContain('!subject:some-topic');
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe('addRaw', () => {
|
|
212
|
+
test('adds an included raw entry', () => {
|
|
213
|
+
const acl = new UserAclBuilder().addRaw('catalogue:uptime').build();
|
|
214
|
+
expect(acl).toContain('catalogue:uptime');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test('adds an excluded raw entry', () => {
|
|
218
|
+
const acl = new UserAclBuilder().addRaw('!catalogue:restricted').build();
|
|
219
|
+
expect(acl).toContain('!catalogue:restricted');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test('ignores invalid raw entries', () => {
|
|
223
|
+
const acl = new UserAclBuilder().addRaw('').build();
|
|
224
|
+
expect(acl).toEqual([]);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe('addMany', () => {
|
|
229
|
+
test('adds multiple entries at once', () => {
|
|
230
|
+
const acl = new UserAclBuilder()
|
|
231
|
+
.addMany(['catalogue:uptime', 'b2b:org-1', '!catalogue:restricted'])
|
|
232
|
+
.build();
|
|
233
|
+
expect(acl).toContain('catalogue:uptime');
|
|
234
|
+
expect(acl).toContain('b2b:org-1');
|
|
235
|
+
expect(acl).toContain('!catalogue:restricted');
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('aclFor', () => {
|
|
241
|
+
test('includes user entry', () => {
|
|
242
|
+
const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: [] });
|
|
243
|
+
expect(acl).toContain('user:user-abc');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test('always includes catalogue:uptime', () => {
|
|
247
|
+
const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: [] });
|
|
248
|
+
expect(acl).toContain('catalogue:uptime');
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test('includes provided catalogue ids', () => {
|
|
252
|
+
const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: ['premium', 'free'] });
|
|
253
|
+
expect(acl).toContain('catalogue:premium');
|
|
254
|
+
expect(acl).toContain('catalogue:free');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test('includes b2b catalogue entry for each organisation', () => {
|
|
258
|
+
const acl = aclFor({ userId: 'user-abc', organisationIds: ['org-1', 'org-2'], catalogueIds: [] });
|
|
259
|
+
expect(acl).toContain('catalogue:b2b:org-1');
|
|
260
|
+
expect(acl).toContain('catalogue:b2b:org-2');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test('includes b2b namespace entry for each organisation', () => {
|
|
264
|
+
const acl = aclFor({ userId: 'user-abc', organisationIds: ['org-1', 'org-2'], catalogueIds: [] });
|
|
265
|
+
expect(acl).toContain('b2b:org-1');
|
|
266
|
+
expect(acl).toContain('b2b:org-2');
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test('includes public by default', () => {
|
|
270
|
+
const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: [] });
|
|
271
|
+
expect(acl).toContain('public');
|
|
272
|
+
});
|
|
273
|
+
});
|