-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathindex.js
188 lines (173 loc) · 7.57 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
'use strict'
const request = require('request');
const url = require('url');
const uaa = require('predix-uaa-client');
const debug = require('debug')('predix-acs-client');
module.exports = (config) => {
if(config) {
config.renew_secs_before = config.renew_secs_before || 60;
}
// Throw exception if required options are missing
let missingConfig = [];
if(!config || !config.uaa || !config.uaa.uri) missingConfig.push('uaa.uri');
if(!config || !config.uaa || !config.uaa.clientId) missingConfig.push('uaa.clientId');
if(!config || !config.uaa || !config.uaa.clientSecret) missingConfig.push('uaa.clientSecret');
if(!config || !config.acsUri) missingConfig.push('acsUri');
if(!config || !config.zoneId) missingConfig.push('zoneId');
if(missingConfig.length > 0) {
const msg = `Required configuration is missing: ${missingConfig.join()}`;
debug(msg);
throw new Error(msg);
}
let acs_utils = {};
// This will fetch and cache an access token for the provided UAA client using the credentials
// that were provided at configuration time.
// If there is already a token which has not expired, that will be returned immediately
/**
* This will fetch and cache an access token for the provided UAA client using the credentials
* that were provided at configuration time.
* If there is already a token which has not expired, that will be returned immediately
*
* @returns {promise} - A promise to provide a token.
* Resolves with the token if successful (or already available).
* Rejected with an error if an error occurs.
*/
acs_utils._getToken = () => {
return new Promise((resolve, reject) => {
uaa.getToken(config.uaa.uri, config.uaa.clientId, config.uaa.clientSecret).then((token) => {
resolve(token.access_token);
}).catch((err) => {
reject(err);
});
});
}
/**
* Checks that the provided user is allowed to perform the action described by the request
* This request is decoupled from the request path, allowing for an alternative description of resources
*
* @param {object} abacRequest - The Attributes Based Access Control request.
* Required properties: subject, resource, action
* @returns {promise} - A promise to authorize the user.
* Resolves with the user and resource attributes.
* Rejected if not authorized, or an error occurs.
*/
acs_utils.isAuthorizedFor = (abacRequest) => {
return new Promise((resolve, reject) => {
if (abacRequest === null || !abacRequest) {
return reject('Parameter: \'abacRequest\' may not be null or undefined.');
}
if (!abacRequest.action || !abacRequest.resourceIdentifier || !abacRequest.subjectIdentifier) {
return reject('Parameter: \'abacRequest\' must contain the properties: action, resourceIdentifier, and subjectIdentifier');
}
// Ensure we have a valid token to talk to ACS
acs_utils._getToken().then((token) => {
// Formulate the request object
const options = {
url: `${config.acsUri}/v1/policy-evaluation`,
headers: {
'cache-control': 'no-cache',
'content-type': 'application/json',
'Predix-Zone-Id': config.zoneId
},
auth: {
bearer: token
},
json: true,
body: abacRequest
};
// Call ACS
request.post(options, (err, resp, data) => {
const statusCode = (resp) ? resp.statusCode : 502;
if(err || statusCode !== 200) {
err = err || `Error getting verdict: ${statusCode}`;
debug('Error getting verdict with request', options, err);
reject(err);
} else {
// Check the 'effect' property
if(data.effect === 'PERMIT') {
resolve(data);
} else {
debug('Not Authorized', options.body, data);
reject(data);
}
}
});
}).catch((err) => {
reject(err);
});
});
}
/**
* Checks that the provided user is allowed to perform the action described by the request
*
* @param {object} req - The request. Compatible with expressjs request.
* Required properties: method, path
* @param {string} username - The subject (or username) of the requester.
* @returns {promise} - A promise to authorize the user.
* Resolves with the user and resource attributes.
* Rejected if not authorized, or an error occurs.
*/
acs_utils.isAuthorized = (req, username, scopes) => {
const abacRequest = {
action: req.method,
resourceIdentifier: req.path,
subjectIdentifier: username
};
// constructing scope-based subject attributes
if (scopes) {
const userGroupsRegEx = config.userGroupsRegEx || 'g.*';
const attributes = scopes.filter(scope => scope.match(userGroupsRegEx))
.map(scope => ({
issuer: 'UAA',
name: 'group',
value: scope
}));
if (attributes.length > 0) {
abacRequest.subjectAttributes = attributes;
}
}
return acs_utils.isAuthorizedFor(abacRequest);
}
/**
* Get subject of provided user from acs
*
* @param {string} username - The subject (or username) of the requester.
* @returns {promise} - A promise to authorize the user.
* Resolves with the user and resource attributes.
* Rejected if not authorized, or an error occurs.
*/
acs_utils.getSubjectAttributes = (subjectIdentifier) => {
return new Promise((resolve, reject) => {
// Ensure we have a valid token to talk to ACS
acs_utils._getToken().then((token) => {
// Formulate the request object
const options = {
url: `${config.acsUri}/v1/subject/${subjectIdentifier}`,
headers: {
'cache-control': 'no-cache',
'content-type': 'application/json',
'Predix-Zone-Id': config.zoneId
},
auth: {
bearer: token
},
json: true
};
// Call ACS
request.get(options, (err, resp, data) => {
const statusCode = (resp) ? resp.statusCode : 502;
if(err || statusCode !== 200) {
err = err || `Error getting subject: ${statusCode}`;
debug('Error getting subject with request', options, err);
reject(err);
} else {
resolve(data);
}
});
}).catch((err) => {
reject(err);
});
});
}
return acs_utils;
}