Skip to content

Commit 2e83f5e

Browse files
Merge pull request #45 from ngraef/multiple-security
Allow multiple securities
2 parents a011dde + 1dbf583 commit 2e83f5e

File tree

8 files changed

+66
-20
lines changed

8 files changed

+66
-20
lines changed

README.MD

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ class PeopleService {
147147

148148
#### @Security
149149

150-
Add a security constraint to method generated docs, refering the security name from securityDefinitions
150+
Add a security constraint to method generated docs, referencing the security name from securityDefinitions.
151+
`@Security` can be used at the controller and method level; if defined on both, method security overwrites controller security.
152+
Multiple security schemes may be specified to require all of them.
151153

152154
```typescript
153155
@Path('people')

src/metadata/controllerGenerator.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,10 @@ export class ControllerGenerator {
8484

8585
const securityDecorators = getDecorators(this.node, decorator => decorator.text === 'Security');
8686
if (!securityDecorators || !securityDecorators.length) { return undefined; }
87-
if (securityDecorators.length > 1) {
88-
throw new Error(`Only one Security decorator allowed in '${this.node.name.text}' controller.`);
89-
}
90-
91-
const d = securityDecorators[0];
9287

93-
return {
88+
return securityDecorators.map(d => ({
9489
name: d.arguments[0],
9590
scopes: d.arguments[1] ? (d.arguments[1] as any).elements.map((e: any) => e.text) : undefined
96-
};
91+
}));
9792
}
9893
}

src/metadata/metadataGenerator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export interface Controller {
9494
consumes: string[];
9595
produces: string[];
9696
tags: string[];
97-
security?: Security;
97+
security?: Security[];
9898
}
9999

100100
export interface Method {
@@ -107,7 +107,7 @@ export interface Method {
107107
type: Type;
108108
tags: string[];
109109
responses: ResponseType[];
110-
security?: Security;
110+
security?: Security[];
111111
summary?: string;
112112
consumes: string[];
113113
produces: string[];

src/metadata/methodGenerator.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -219,16 +219,11 @@ export class MethodGenerator {
219219
private getMethodSecurity() {
220220
const securityDecorators = getDecorators(this.node, decorator => decorator.text === 'Security');
221221
if (!securityDecorators || !securityDecorators.length) { return undefined; }
222-
if (securityDecorators.length > 1) {
223-
throw new Error(`Only one Security decorator allowed in '${this.getCurrentLocation}' method.`);
224-
}
225-
226-
const d = securityDecorators[0];
227222

228-
return {
223+
return securityDecorators.map(d => ({
229224
name: d.arguments[0],
230225
scopes: d.arguments[1] ? (d.arguments[1] as any).elements.map((e: any) => e.text) : undefined
231-
};
226+
}));
232227
}
233228

234229
private getInitializerValue(initializer: any) {

src/swagger/generator.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,9 @@ export class SpecGenerator {
120120
if (method.deprecated) { pathMethod.deprecated = method.deprecated; }
121121
if (method.tags.length) { pathMethod.tags = method.tags; }
122122
if (method.security) {
123-
const security: any = {};
124-
security[method.security.name] = method.security.scopes ? method.security.scopes : [];
125-
pathMethod.security = [security];
123+
pathMethod.security = method.security.map(s => ({
124+
[s.name]: s.scopes || []
125+
}));
126126
}
127127
this.handleMethodConsumes(method, pathMethod);
128128

test/data/apis.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,3 +358,28 @@ export class AbstractEntityEndpoint {
358358
return new NamedEntity();
359359
}
360360
}
361+
362+
@Path('secure')
363+
@swagger.Security('access_token')
364+
export class SecureEndpoint {
365+
@GET
366+
get(): string {
367+
return 'Access Granted';
368+
}
369+
370+
@POST
371+
@swagger.Security('user_email')
372+
post(): string {
373+
return 'Posted';
374+
}
375+
}
376+
377+
@Path('supersecure')
378+
@swagger.Security('access_token')
379+
@swagger.Security('user_email')
380+
export class SuperSecureEndpoint {
381+
@GET
382+
get(): string {
383+
return 'Access Granted';
384+
}
385+
}

test/data/swagger.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@
1414
"type": "apiKey",
1515
"name": "access_token",
1616
"in": "query"
17+
},
18+
"access_token": {
19+
"type": "apiKey",
20+
"name": "authorization",
21+
"in": "header"
22+
},
23+
"user_email": {
24+
"type": "apiKey",
25+
"name": "x-user-email",
26+
"in": "header"
1727
}
1828
},
1929
"spec": {

test/unit/definitions.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,4 +304,23 @@ describe('Definition generation', () => {
304304
expect(expression.evaluate(spec)).to.eq('A numeric identifier');
305305
});
306306
});
307+
308+
describe('SecureEndpoint', () => {
309+
it('should apply controller security to request', () => {
310+
const expression = jsonata('paths."/secure".get.security');
311+
expect(expression.evaluate(spec)).to.deep.equal([ { 'access_token': [] } ]);
312+
});
313+
314+
it('method security should override controller security', () => {
315+
const expression = jsonata('paths."/secure".post.security');
316+
expect(expression.evaluate(spec)).to.deep.equal([ { 'user_email': [] } ]);
317+
});
318+
});
319+
320+
describe('SuperSecureEndpoint', () => {
321+
it('should apply two controller securities to request', () => {
322+
const expression = jsonata('paths."/supersecure".get.security');
323+
expect(expression.evaluate(spec)).to.deep.equal([ { 'access_token': [] }, { 'user_email': [] } ]);
324+
});
325+
});
307326
});

0 commit comments

Comments
 (0)