-
Notifications
You must be signed in to change notification settings - Fork 122
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add more extensive permission parsing support
- Loading branch information
Showing
11 changed files
with
221 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { Operation } from '../operations/Operation'; | ||
import { PermissionSet } from './PermissionSet'; | ||
import { PermissionsExtractor } from './PermissionsExtractor'; | ||
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError'; | ||
|
||
/** | ||
* Generates permissions for the base set of methods that always require the same permissions. | ||
* Specifically: GET, HEAD, POST, PUT and DELETE. | ||
*/ | ||
export class BasePermissionsExtractor extends PermissionsExtractor { | ||
public async canHandle(input: Operation): Promise<void> { | ||
if (![ 'HEAD', 'GET', 'POST', 'PUT', 'DELETE' ].includes(input.method)) { | ||
throw new UnsupportedHttpError('Only HEAD, GET, POST, PUT and DELETE are supported.'); | ||
} | ||
} | ||
|
||
public async handle(input: Operation): Promise<PermissionSet> { | ||
const result = { | ||
read: input.method === 'HEAD' || input.method === 'GET', | ||
append: false, | ||
write: input.method === 'POST' || input.method === 'PUT' || input.method === 'DELETE', | ||
}; | ||
result.append = result.write; | ||
return result; | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { Algebra } from 'sparqlalgebrajs'; | ||
import { Operation } from '../operations/Operation'; | ||
import { PermissionSet } from './PermissionSet'; | ||
import { PermissionsExtractor } from './PermissionsExtractor'; | ||
import { Representation } from '../representation/Representation'; | ||
import { SparqlUpdatePatch } from '../http/SparqlUpdatePatch'; | ||
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError'; | ||
|
||
/** | ||
* Generates permissions for a SPARQL DELETE/INSERT PATCH. | ||
* Updates with only an INSERT can be done with just append permissions, | ||
* while DELETEs require write permissions as well. | ||
*/ | ||
export class SparqlPatchPermissionsExtractor extends PermissionsExtractor { | ||
public async canHandle(input: Operation): Promise<void> { | ||
if (input.method !== 'PATCH') { | ||
throw new UnsupportedHttpError('Only PATCH operations are supported.'); | ||
} | ||
if (!input.body) { | ||
throw new UnsupportedHttpError('PATCH body is required to determine permissions.'); | ||
} | ||
if (!this.isSparql(input.body)) { | ||
throw new UnsupportedHttpError('Only SPARQL update PATCHes are supported.'); | ||
} | ||
if (!this.isDeleteInsert(input.body.algebra)) { | ||
throw new UnsupportedHttpError('Only DELETE/INSERT SPARQL update operations are supported.'); | ||
} | ||
} | ||
|
||
public async handle(input: Operation): Promise<PermissionSet> { | ||
if (!input.body || !this.isSparql(input.body) || !this.isDeleteInsert(input.body.algebra)) { | ||
throw new UnsupportedHttpError('A SPARQL DELETE/INSERT body is required.'); | ||
} | ||
const result = { | ||
read: false, | ||
append: this.needsAppend(input.body.algebra), | ||
write: this.needsWrite(input.body.algebra), | ||
}; | ||
result.append = result.append || result.write; | ||
return result; | ||
} | ||
|
||
private isSparql(data: Representation): data is SparqlUpdatePatch { | ||
return Boolean((data as SparqlUpdatePatch).algebra); | ||
} | ||
|
||
private isDeleteInsert(op: Algebra.Operation): op is Algebra.DeleteInsert { | ||
return op.type === Algebra.types.DELETE_INSERT; | ||
} | ||
|
||
private needsAppend(update: Algebra.DeleteInsert): boolean { | ||
return Boolean(update.insert && update.insert.length > 0); | ||
} | ||
|
||
private needsWrite(update: Algebra.DeleteInsert): boolean { | ||
return Boolean(update.delete && update.delete.length > 0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
test/unit/ldp/permissions/BasePermissionsExtractor.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { BasePermissionsExtractor } from '../../../../src/ldp/permissions/BasePermissionsExtractor'; | ||
import { Operation } from '../../../../src/ldp/operations/Operation'; | ||
import { UnsupportedHttpError } from '../../../../src/util/errors/UnsupportedHttpError'; | ||
|
||
describe('A BasePermissionsExtractor', (): void => { | ||
const extractor = new BasePermissionsExtractor(); | ||
|
||
it('can handle HEAD/GET/POST/PUT/DELETE.', async(): Promise<void> => { | ||
await expect(extractor.canHandle({ method: 'HEAD' } as Operation)).resolves.toBeUndefined(); | ||
await expect(extractor.canHandle({ method: 'GET' } as Operation)).resolves.toBeUndefined(); | ||
await expect(extractor.canHandle({ method: 'POST' } as Operation)).resolves.toBeUndefined(); | ||
await expect(extractor.canHandle({ method: 'PUT' } as Operation)).resolves.toBeUndefined(); | ||
await expect(extractor.canHandle({ method: 'DELETE' } as Operation)).resolves.toBeUndefined(); | ||
await expect(extractor.canHandle({ method: 'PATCH' } as Operation)).rejects.toThrow(UnsupportedHttpError); | ||
}); | ||
|
||
it('requires read for HEAD operations.', async(): Promise<void> => { | ||
await expect(extractor.handle({ method: 'HEAD' } as Operation)).resolves.toEqual({ | ||
read: true, | ||
append: false, | ||
write: false, | ||
}); | ||
}); | ||
|
||
it('requires read for GET operations.', async(): Promise<void> => { | ||
await expect(extractor.handle({ method: 'GET' } as Operation)).resolves.toEqual({ | ||
read: true, | ||
append: false, | ||
write: false, | ||
}); | ||
}); | ||
|
||
it('requires write for POST operations.', async(): Promise<void> => { | ||
await expect(extractor.handle({ method: 'POST' } as Operation)).resolves.toEqual({ | ||
read: false, | ||
append: true, | ||
write: true, | ||
}); | ||
}); | ||
|
||
it('requires write for PUT operations.', async(): Promise<void> => { | ||
await expect(extractor.handle({ method: 'PUT' } as Operation)).resolves.toEqual({ | ||
read: false, | ||
append: true, | ||
write: true, | ||
}); | ||
}); | ||
|
||
it('requires write for DELETE operations.', async(): Promise<void> => { | ||
await expect(extractor.handle({ method: 'DELETE' } as Operation)).resolves.toEqual({ | ||
read: false, | ||
append: true, | ||
write: true, | ||
}); | ||
}); | ||
}); |
42 changes: 0 additions & 42 deletions
42
test/unit/ldp/permissions/SimplePermissionsExtractor.test.ts
This file was deleted.
Oops, something went wrong.
62 changes: 62 additions & 0 deletions
62
test/unit/ldp/permissions/SparqlPatchPermissionsExtractor.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { Factory } from 'sparqlalgebrajs'; | ||
import { Operation } from '../../../../src/ldp/operations/Operation'; | ||
import { SparqlPatchPermissionsExtractor } from '../../../../src/ldp/permissions/SparqlPatchPermissionsExtractor'; | ||
import { SparqlUpdatePatch } from '../../../../src/ldp/http/SparqlUpdatePatch'; | ||
import { UnsupportedHttpError } from '../../../../src/util/errors/UnsupportedHttpError'; | ||
|
||
describe('A SparqlPatchPermissionsExtractor', (): void => { | ||
const extractor = new SparqlPatchPermissionsExtractor(); | ||
const factory = new Factory(); | ||
|
||
it('can only handle SPARQL DELETE/INSERT PATCH operations.', async(): Promise<void> => { | ||
const operation = { method: 'PATCH', body: { algebra: factory.createDeleteInsert() }} as unknown as Operation; | ||
await expect(extractor.canHandle(operation)).resolves.toBeUndefined(); | ||
await expect(extractor.canHandle({ ...operation, method: 'GET' })) | ||
.rejects.toThrow(new UnsupportedHttpError('Only PATCH operations are supported.')); | ||
await expect(extractor.canHandle({ ...operation, body: undefined })) | ||
.rejects.toThrow(new UnsupportedHttpError('PATCH body is required to determine permissions.')); | ||
await expect(extractor.canHandle({ ...operation, body: {} as SparqlUpdatePatch })) | ||
.rejects.toThrow(new UnsupportedHttpError('Only SPARQL update PATCHes are supported.')); | ||
await expect(extractor.canHandle({ ...operation, | ||
body: { algebra: factory.createMove('DEFAULT', 'DEFAULT') } as unknown as SparqlUpdatePatch })) | ||
.rejects.toThrow(new UnsupportedHttpError('Only DELETE/INSERT SPARQL update operations are supported.')); | ||
}); | ||
|
||
it('errors if input for the handle function is missing.', async(): Promise<void> => { | ||
const operation = { method: 'PATCH', body: { algebra: factory.createDeleteInsert() }} as unknown as Operation; | ||
await expect(extractor.handle({ ...operation, body: undefined })).rejects.toThrow(UnsupportedHttpError); | ||
await expect(extractor.handle({ ...operation, body: {} as SparqlUpdatePatch })) | ||
.rejects.toThrow(UnsupportedHttpError); | ||
await expect(extractor.handle({ ...operation, | ||
body: { algebra: factory.createMove('DEFAULT', 'DEFAULT') } as unknown as SparqlUpdatePatch })) | ||
.rejects.toThrow(UnsupportedHttpError); | ||
}); | ||
|
||
it('requires append for INSERT operations.', async(): Promise<void> => { | ||
const operation = { | ||
method: 'PATCH', | ||
body: { algebra: factory.createDeleteInsert(undefined, [ | ||
factory.createPattern(factory.createTerm('<s>'), factory.createTerm('<p>'), factory.createTerm('<o>')), | ||
]) }, | ||
} as unknown as Operation; | ||
await expect(extractor.handle(operation)).resolves.toEqual({ | ||
read: false, | ||
append: true, | ||
write: false, | ||
}); | ||
}); | ||
|
||
it('requires write for DELETE operations.', async(): Promise<void> => { | ||
const operation = { | ||
method: 'PATCH', | ||
body: { algebra: factory.createDeleteInsert([ | ||
factory.createPattern(factory.createTerm('<s>'), factory.createTerm('<p>'), factory.createTerm('<o>')), | ||
]) }, | ||
} as unknown as Operation; | ||
await expect(extractor.handle(operation)).resolves.toEqual({ | ||
read: false, | ||
append: true, | ||
write: true, | ||
}); | ||
}); | ||
}); |