Skip to content

Commit 6d038c3

Browse files
authored
Merge pull request #106 from getlarge/105-perfketo-client-wrapper-improve-authorizationguard-performance-by-combining-permission-checks
perf: improve OryAuthorizationGuard performance by combining permission checks
2 parents 6226105 + 2ee37f1 commit 6d038c3

File tree

10 files changed

+395
-85
lines changed

10 files changed

+395
-85
lines changed

.github/workflows/ci.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ jobs:
4040
with:
4141
fetch-depth: 0
4242

43-
# Cache node_modules
4443
- uses: actions/setup-node@v4
4544
with:
4645
node-version: 20

packages/keto-client-wrapper/src/lib/ory-authorization.guard.spec.ts

Lines changed: 188 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ describe('OryAuthorizationGuard', () => {
3737
OryPermissionsService,
3838
{
3939
provide: OryPermissionsModuleOptions,
40-
useValue: { basePath: 'http://localhost' },
40+
useValue: {
41+
basePath: 'http://localhost',
42+
supportBatchPermissionCheck: true,
43+
},
4144
},
4245
{
4346
provide: OryBaseService,
@@ -69,6 +72,11 @@ describe('OryAuthorizationGuard', () => {
6972

7073
it('should allow access for a single permitted relation tuple', async () => {
7174
const factory = () => 'user:123#access@resource:456';
75+
Object.defineProperty(
76+
oryPermissionsService,
77+
'supportBatchPermissionCheck',
78+
{ value: false }
79+
);
7280
jest
7381
.spyOn(oryPermissionsService, 'checkPermission')
7482
.mockResolvedValue(mockAxiosResponse({ allowed: true }));
@@ -89,9 +97,12 @@ describe('OryAuthorizationGuard', () => {
8997
],
9098
} satisfies EnhancedRelationTupleFactory;
9199
jest
92-
.spyOn(oryPermissionsService, 'checkPermission')
93-
.mockResolvedValueOnce(mockAxiosResponse({ allowed: true }))
94-
.mockResolvedValueOnce(mockAxiosResponse({ allowed: false }));
100+
.spyOn(oryPermissionsService, 'batchCheckPermission')
101+
.mockResolvedValueOnce(
102+
mockAxiosResponse({
103+
results: [{ allowed: false }, { allowed: true }],
104+
})
105+
);
95106

96107
const result = await oryAuthorizationGuard.evaluateConditions(
97108
factory,
@@ -109,9 +120,12 @@ describe('OryAuthorizationGuard', () => {
109120
],
110121
} satisfies EnhancedRelationTupleFactory;
111122
jest
112-
.spyOn(oryPermissionsService, 'checkPermission')
113-
.mockResolvedValueOnce(mockAxiosResponse({ allowed: false }))
114-
.mockResolvedValueOnce(mockAxiosResponse({ allowed: true }));
123+
.spyOn(oryPermissionsService, 'batchCheckPermission')
124+
.mockResolvedValueOnce(
125+
mockAxiosResponse({
126+
results: [{ allowed: false }, { allowed: true }],
127+
})
128+
);
115129

116130
const result = await oryAuthorizationGuard.evaluateConditions(
117131
factory,
@@ -120,7 +134,7 @@ describe('OryAuthorizationGuard', () => {
120134
expect(result.allowed).toBe(true);
121135
});
122136

123-
it('should correctly evaluate nested AND/OR conditions', async () => {
137+
it('should correctly evaluate nested AND/OR conditions - 1', async () => {
124138
const factory = {
125139
type: 'AND',
126140
conditions: [
@@ -135,10 +149,172 @@ describe('OryAuthorizationGuard', () => {
135149
],
136150
} satisfies EnhancedRelationTupleFactory;
137151
jest
138-
.spyOn(oryPermissionsService, 'checkPermission')
139-
.mockResolvedValueOnce(mockAxiosResponse({ allowed: true }))
140-
.mockResolvedValueOnce(mockAxiosResponse({ allowed: true }))
141-
.mockResolvedValueOnce(mockAxiosResponse({ allowed: true }));
152+
.spyOn(oryPermissionsService, 'batchCheckPermission')
153+
.mockResolvedValueOnce(
154+
mockAxiosResponse({
155+
results: [{ allowed: true }, { allowed: false }, { allowed: true }],
156+
})
157+
);
158+
159+
const result = await oryAuthorizationGuard.evaluateConditions(
160+
factory,
161+
context
162+
);
163+
expect(result.allowed).toBe(true);
164+
});
165+
166+
it('should correctly evaluate nested AND/OR conditions - 2', async () => {
167+
const factory = {
168+
type: 'AND',
169+
conditions: [
170+
{
171+
type: 'OR',
172+
conditions: [
173+
() => 'user:123#access@resource:456',
174+
() => 'user:123#access@resource:789',
175+
],
176+
},
177+
() => 'user:123#access@resource:101',
178+
],
179+
} satisfies EnhancedRelationTupleFactory;
180+
jest
181+
.spyOn(oryPermissionsService, 'batchCheckPermission')
182+
.mockResolvedValueOnce(
183+
mockAxiosResponse({
184+
results: [
185+
{ allowed: false },
186+
{ allowed: false },
187+
{ allowed: true },
188+
],
189+
})
190+
);
191+
192+
const result = await oryAuthorizationGuard.evaluateConditions(
193+
factory,
194+
context
195+
);
196+
expect(result.allowed).toBe(false);
197+
});
198+
199+
it('should correctly evaluate nested AND/OR conditions - 3', async () => {
200+
const factory = {
201+
type: 'AND',
202+
conditions: [
203+
{
204+
type: 'OR',
205+
conditions: [
206+
() => 'user:123#access@resource:456',
207+
() => 'user:123#access@resource:789',
208+
],
209+
},
210+
() => 'user:123#access@resource:101',
211+
],
212+
} satisfies EnhancedRelationTupleFactory;
213+
jest
214+
.spyOn(oryPermissionsService, 'batchCheckPermission')
215+
.mockResolvedValueOnce(
216+
mockAxiosResponse({
217+
results: [
218+
{ allowed: true },
219+
{ allowed: false },
220+
{ allowed: false },
221+
],
222+
})
223+
);
224+
225+
const result = await oryAuthorizationGuard.evaluateConditions(
226+
factory,
227+
context
228+
);
229+
expect(result.allowed).toBe(false);
230+
});
231+
232+
it('should correctly evaluate nested AND/OR conditions - 4', async () => {
233+
const factory = {
234+
type: 'AND',
235+
conditions: [
236+
{
237+
type: 'OR',
238+
conditions: [
239+
{
240+
type: 'OR',
241+
conditions: [
242+
() => 'user:123#access@resource:456',
243+
() => 'user:123#access@resource:457',
244+
],
245+
},
246+
{
247+
type: 'AND',
248+
conditions: [
249+
() => 'user:123#access@resource:459',
250+
() => 'user:123#access@resource:460',
251+
],
252+
},
253+
],
254+
},
255+
() => 'user:123#access@resource:101',
256+
],
257+
} satisfies EnhancedRelationTupleFactory;
258+
jest
259+
.spyOn(oryPermissionsService, 'batchCheckPermission')
260+
.mockResolvedValueOnce(
261+
mockAxiosResponse({
262+
results: [
263+
{ allowed: true },
264+
{ allowed: false },
265+
{ allowed: false },
266+
{ allowed: false },
267+
{ allowed: true },
268+
],
269+
})
270+
);
271+
272+
const result = await oryAuthorizationGuard.evaluateConditions(
273+
factory,
274+
context
275+
);
276+
expect(result.allowed).toBe(true);
277+
});
278+
279+
it('should correctly evaluate nested AND/OR conditions - 5', async () => {
280+
const factory = {
281+
type: 'AND',
282+
conditions: [
283+
{
284+
type: 'OR',
285+
conditions: [
286+
{
287+
type: 'OR',
288+
conditions: [
289+
() => 'user:123#access@resource:456',
290+
() => 'user:123#access@resource:457',
291+
],
292+
},
293+
{
294+
type: 'AND',
295+
conditions: [
296+
() => 'user:123#access@resource:459',
297+
() => 'user:123#access@resource:460',
298+
],
299+
},
300+
],
301+
},
302+
() => 'user:123#access@resource:101',
303+
],
304+
} satisfies EnhancedRelationTupleFactory;
305+
jest
306+
.spyOn(oryPermissionsService, 'batchCheckPermission')
307+
.mockResolvedValueOnce(
308+
mockAxiosResponse({
309+
results: [
310+
{ allowed: true },
311+
{ allowed: false },
312+
{ allowed: true },
313+
{ allowed: true },
314+
{ allowed: true },
315+
],
316+
})
317+
);
142318

143319
const result = await oryAuthorizationGuard.evaluateConditions(
144320
factory,

0 commit comments

Comments
 (0)