Skip to content
This repository was archived by the owner on Nov 8, 2024. It is now read-only.

Commit 07bfb18

Browse files
authored
Merge pull request #206 from apiaryio/pksunkara/security
Finished scheme support
2 parents 35f97c5 + 1587583 commit 07bfb18

20 files changed

+1164
-58
lines changed

packages/api-elements/lib/elements/AuthScheme.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class AuthScheme extends ArrayElement {
1818
/**
1919
* @name transitions
2020
* @type ArraySlice
21-
* @memberof HttpMessagePayload.prototype
21+
* @memberof AuthScheme.prototype
2222
*/
2323
get transitions() {
2424
return this.children.filter(item => item.element === 'transition');
@@ -27,11 +27,25 @@ class AuthScheme extends ArrayElement {
2727
/**
2828
* @name members
2929
* @type ArraySlice
30-
* @memberof HttpMessagePayload.prototype
30+
* @memberof AuthScheme.prototype
3131
*/
3232
get members() {
3333
return this.children.filter(item => item.element === 'member');
3434
}
35+
36+
/**
37+
* @name grantTypeValue
38+
* @memberof AuthScheme.prototype
39+
*/
40+
get grantTypeValue() {
41+
const grantType = this.members.find(item => item.key.toValue() === 'grantType');
42+
43+
if (grantType && grantType.value) {
44+
return grantType.value.toValue();
45+
}
46+
47+
return undefined;
48+
}
3549
}
3650

3751
module.exports = AuthScheme;

packages/api-elements/test/api-description-test.js

+64-1
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,19 @@ describe('API description namespace', () => {
10031003
},
10041004
},
10051005
},
1006+
{
1007+
element: 'member',
1008+
content: {
1009+
key: {
1010+
element: 'string',
1011+
content: 'grantType',
1012+
},
1013+
value: {
1014+
element: 'string',
1015+
content: 'token',
1016+
},
1017+
},
1018+
},
10061019
{
10071020
element: 'transition',
10081021
attributes: {
@@ -1039,7 +1052,7 @@ describe('API description namespace', () => {
10391052

10401053
it('should contain members', () => {
10411054
const { members } = authScheme;
1042-
expect(members).to.have.length(1);
1055+
expect(members).to.have.length(2);
10431056
members.forEach((item) => {
10441057
expect(item).to.be.an.instanceof(MemberElement);
10451058
});
@@ -1052,6 +1065,56 @@ describe('API description namespace', () => {
10521065
expect(item).to.be.an.instanceof(Transition);
10531066
});
10541067
});
1068+
1069+
it('should retrieve grant type', () => {
1070+
expect(authScheme.grantTypeValue).to.equal('token');
1071+
});
1072+
1073+
it('without grant type should return undefined', () => {
1074+
const refracted = {
1075+
element: 'Token Auth Scheme',
1076+
meta: {
1077+
id: {
1078+
element: 'string',
1079+
content: 'Custom Token Auth',
1080+
},
1081+
},
1082+
content: [],
1083+
};
1084+
1085+
const element = namespace.fromRefract(refracted);
1086+
const authScheme = new AuthScheme(element.content, element.meta, element.attributes);
1087+
1088+
expect(authScheme.grantTypeValue).to.be.undefined;
1089+
});
1090+
1091+
it('without grant type value should return undefined', () => {
1092+
const refracted = {
1093+
element: 'Token Auth Scheme',
1094+
meta: {
1095+
id: {
1096+
element: 'string',
1097+
content: 'Custom Token Auth',
1098+
},
1099+
},
1100+
content: [
1101+
{
1102+
element: 'member',
1103+
content: {
1104+
key: {
1105+
element: 'string',
1106+
content: 'grantType',
1107+
},
1108+
},
1109+
},
1110+
],
1111+
};
1112+
1113+
const element = namespace.fromRefract(refracted);
1114+
const authScheme = new AuthScheme(element.content, element.meta, element.attributes);
1115+
1116+
expect(authScheme.grantTypeValue).to.be.undefined;
1117+
});
10551118
});
10561119

10571120
context('HTTP transaction element', () => {

packages/fury-adapter-oas3-parser/STATUS.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Key:
7272
| responses | [~](#responses-object) |
7373
| callbacks | [](https://github.com/apiaryio/api-elements.js/issues/74) |
7474
| deprecated ||
75-
| security | [](https://github.com/apiaryio/api-elements.js/issues/77) |
75+
| security | [](https://github.com/apiaryio/api-elements.js/issues/329) |
7676
| servers | [](https://github.com/apiaryio/api-elements.js/issues/76) |
7777

7878
## Parameter Object

packages/fury-adapter-oas3-parser/lib/parser/oas/parseComponentsObject.js

+14-3
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,20 @@ function parseComponentsObject(context, element) {
156156

157157
object.forEach((value, key) => {
158158
if (value) {
159-
// eslint-disable-next-line no-param-reassign
160-
value.meta.id = key.clone();
161-
array.push(value);
159+
if (value instanceof namespace.elements.AuthScheme) {
160+
// eslint-disable-next-line no-param-reassign
161+
value.id = key.clone();
162+
array.push(value);
163+
164+
return;
165+
}
166+
167+
// append oauth2 flow names
168+
value.forEach((flow) => {
169+
// eslint-disable-next-line no-param-reassign
170+
flow.id = `${key.toValue()} ${flow.grantTypeValue}`;
171+
array.push(flow);
172+
});
162173
}
163174
});
164175

packages/fury-adapter-oas3-parser/lib/parser/oas/parseOauthFlowObject.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,25 @@ function parseOauthFlowObject(context, object) {
4040
return scope;
4141
}));
4242

43+
const parseUrl = pipeParseResult(namespace,
44+
parseString(context, name, false),
45+
(url) => {
46+
const transition = new namespace.elements.Transition();
47+
48+
transition.href = url.value.clone();
49+
transition.relation = url.key.clone();
50+
51+
// remove 'Url' from key
52+
transition.relation.content = transition.relation.toValue().slice(0, -3);
53+
54+
return transition;
55+
});
56+
4357
const parseMember = R.cond([
4458
[hasKey('scopes'), R.compose(parseScopes, getValue)],
45-
[hasKey('refreshUrl'), parseString(context, name, false)],
46-
[hasKey('authorizationUrl'), parseString(context, name, false)],
47-
[hasKey('tokenUrl'), parseString(context, name, false)],
59+
[hasKey('refreshUrl'), parseUrl],
60+
[hasKey('authorizationUrl'), parseUrl],
61+
[hasKey('tokenUrl'), parseUrl],
4862

4963
// FIXME Support exposing extensions into parse result
5064
[isExtension, () => new namespace.elements.ParseResult()],

packages/fury-adapter-oas3-parser/lib/parser/oas/parseOauthFlowsObject.js

+4
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ function parseOauthFlowsObject(context, object) {
7272
authScheme.push(new namespace.elements.Member('grantType', grantTypes[member.key.toValue()]));
7373
authScheme.push(member.value.getMember('scopes'));
7474

75+
R.filter(R.is(namespace.elements.Transition), member.value).forEach((item) => {
76+
authScheme.push(item);
77+
});
78+
7579
return authScheme;
7680
}));
7781

packages/fury-adapter-oas3-parser/lib/parser/oas/parseOpenAPIObject.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,23 @@ function parseOASObject(context, object) {
125125
parseObject(context, name, parseMember, requiredKeys, ['components']),
126126
(object) => {
127127
const api = object.get('info');
128+
const components = object.get('components');
129+
130+
if (components) {
131+
const schemes = R.or(components.get('securitySchemes'), new namespace.elements.Array());
132+
133+
if (!schemes.isEmpty) {
134+
api.push(new namespace.elements.Category(
135+
schemes.content, { classes: ['authSchemes'] }
136+
));
137+
}
138+
}
128139

129140
const resources = object.get('paths');
130141
if (resources) {
131142
api.content = api.content.concat(resources.content);
132143
}
133144

134-
const components = object.get('components');
135145
if (components) {
136146
const schemas = R.or(components.get('schemas'), new namespace.elements.Array())
137147
.content

packages/fury-adapter-oas3-parser/lib/parser/oas/parseOperationObject.js

+30-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
const R = require('ramda');
22
const {
3-
isMember, isExtension, hasKey, getValue,
3+
isArray, isMember, isExtension, hasKey, getValue,
44
} = require('../../predicates');
55
const {
6+
createWarning,
67
createUnsupportedMemberWarning,
78
createInvalidMemberWarning,
89
createIdentifierNotUniqueWarning,
@@ -13,6 +14,7 @@ const parseObject = require('../parseObject');
1314
const parseString = require('../parseString');
1415
const parseResponsesObject = require('./parseResponsesObject');
1516
const parseParameterObjects = require('./parseParameterObjects');
17+
const parseSecurityRequirementObject = require('./parseSecurityRequirementObject');
1618
const parseRequestBodyObject = require('./parseRequestBodyObject');
1719
const parseReference = require('../parseReference');
1820

@@ -21,7 +23,7 @@ const parseRequestBodyObjectOrRef = parseReference('requestBodies', parseRequest
2123
const name = 'Operation Object';
2224
const requiredKeys = ['responses'];
2325
const unsupportedKeys = [
24-
'tags', 'externalDocs', 'callbacks', 'deprecated', 'security',
26+
'tags', 'externalDocs', 'callbacks', 'deprecated',
2527
];
2628
const isUnsupportedKey = R.anyPass(R.map(hasKey, unsupportedKeys));
2729

@@ -102,13 +104,32 @@ function parseOperationObject(context, path, member) {
102104
),
103105
]));
104106

107+
const parseSecurity = pipeParseResult(namespace,
108+
R.unless(isArray, createWarning(namespace, `'${name}' 'security' is not an array`)),
109+
R.compose(R.chain(parseSecurityRequirementObject(context)), R.constructN(1, namespace.elements.Array)),
110+
requirements => requirements.map((requirement) => {
111+
if (requirement.length === 1) {
112+
return requirement.get(0);
113+
}
114+
115+
const link = new namespace.elements.Link();
116+
link.relation = 'profile';
117+
link.href = 'https://github.com/refractproject/rfcs/issues/39';
118+
119+
const allOf = new namespace.elements.Extension(requirement.content);
120+
allOf.meta.set('links', new namespace.elements.Array([link]));
121+
122+
return allOf;
123+
}));
124+
105125
const parseMember = R.cond([
106126
[hasKey('summary'), parseString(context, name, false)],
107127
[hasKey('description'), parseCopy(context, name, false)],
108128
[hasKey('operationId'), pipeParseResult(namespace, parseString(context, name, false), parseOperationId)],
109129
[hasKey('responses'), R.compose(parseResponsesObject(context), getValue)],
110130
[hasKey('requestBody'), R.compose(parseRequestBodyObjectOrRef(context), getValue)],
111131
[hasKey('parameters'), R.compose(parseParameterObjects(context, name), getValue)],
132+
[hasKey('security'), R.compose(parseSecurity, getValue)],
112133

113134
[isUnsupportedKey, createUnsupportedMemberWarning(namespace, name)],
114135

@@ -161,6 +182,13 @@ function parseOperationObject(context, path, member) {
161182
}
162183
}
163184

185+
const security = operation.get('security');
186+
if (security) {
187+
transactions.forEach((transaction) => {
188+
transaction.attributes.set('authSchemes', security.clone());
189+
});
190+
}
191+
164192
return transition;
165193
});
166194

packages/fury-adapter-oas3-parser/lib/parser/oas/parseSecurityRequirementObject.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,17 @@ function parseSecurityRequirementObject(context, object) {
4040
const parseSecurityRequirement = pipeParseResult(namespace,
4141
parseObject(context, name, parseMember),
4242
(securityRequirement) => {
43+
// TODO: expand oauth requirements into multiples depending on flows
4344
const arr = new namespace.elements.Array([]);
4445

4546
securityRequirement.forEach((value, key) => {
4647
let e;
4748
const scopes = value.map(scope => scope.toValue());
4849

4950
if (scopes.length) {
50-
e = new namespace.elements.Object({ scopes });
51+
e = new namespace.elements.AuthScheme({ scopes });
5152
} else {
52-
e = new namespace.elements.Object({});
53+
e = new namespace.elements.AuthScheme({});
5354
}
5455

5556
e.element = key.toValue();

0 commit comments

Comments
 (0)