ef-keycloak-connect 1.2.4 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/services/keycloakService.js +486 -125
- package/services/teamsService.js +141 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ef-keycloak-connect",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Node JS keycloak adapter for authentication and authorization.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -19,4 +19,4 @@
|
|
|
19
19
|
"joi": "^17.6.0",
|
|
20
20
|
"keycloak-connect": "^10.0.2"
|
|
21
21
|
}
|
|
22
|
-
}
|
|
22
|
+
}
|
|
@@ -6,15 +6,18 @@ var memory = new session.MemoryStore();
|
|
|
6
6
|
var keycloakConfig = null;
|
|
7
7
|
|
|
8
8
|
const FinesseService = require('./finesseService');
|
|
9
|
+
const TeamsService = require('./teamsService');
|
|
10
|
+
|
|
9
11
|
const finesseService = new FinesseService();
|
|
12
|
+
const teamsService = new TeamsService();
|
|
10
13
|
|
|
11
|
-
class KeycloakService extends Keycloak{
|
|
14
|
+
class KeycloakService extends Keycloak {
|
|
12
15
|
|
|
13
16
|
constructor(config) {
|
|
14
17
|
|
|
15
|
-
keycloakConfig =
|
|
18
|
+
keycloakConfig = { ...config };
|
|
16
19
|
super({ store: memory }, keycloakConfig); //initialising keycloak-connect //Keycloak = new Keycloak({store: memory}, config);
|
|
17
|
-
|
|
20
|
+
// this.keycloakConfig = config;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
//Based on the attributes it either authenticate keycloak user or finesse user.
|
|
@@ -23,23 +26,23 @@ class KeycloakService extends Keycloak{
|
|
|
23
26
|
let token = '';
|
|
24
27
|
|
|
25
28
|
// If finesseUrl is empty it means normal keycloak auth is required.
|
|
26
|
-
if(finesseUrl == ''){
|
|
29
|
+
if (finesseUrl == '') {
|
|
27
30
|
|
|
28
31
|
token = await this.getKeycloakTokenWithIntrospect(user_name, user_password, realm_name);
|
|
29
32
|
return token;
|
|
30
33
|
|
|
31
|
-
}else{
|
|
34
|
+
} else {
|
|
32
35
|
|
|
33
36
|
// Finesse Auth, takes userRole in argument to create user along with role.
|
|
34
37
|
token = await this.authenticateFinesse(user_name, user_password, finesseUrl, userRoles, finesseToken)
|
|
35
38
|
return token;
|
|
36
|
-
|
|
39
|
+
|
|
37
40
|
}
|
|
38
|
-
|
|
41
|
+
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
getAccessToken(user_name, user_password){
|
|
42
|
-
|
|
44
|
+
getAccessToken(user_name, user_password) {
|
|
45
|
+
|
|
43
46
|
return new Promise(async (resolve, reject) => {
|
|
44
47
|
|
|
45
48
|
var URL = keycloakConfig["auth-server-url"] + 'realms/' + keycloakConfig["realm"] + '/protocol/openid-connect/token';
|
|
@@ -74,7 +77,7 @@ class KeycloakService extends Keycloak{
|
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
// this function requires an Admin user in keycloak.json having realm-management roles
|
|
77
|
-
async getKeycloakTokenWithIntrospect(user_name, user_password, realm_name){
|
|
80
|
+
async getKeycloakTokenWithIntrospect(user_name, user_password, realm_name) {
|
|
78
81
|
|
|
79
82
|
return new Promise(async (resolve, reject) => {
|
|
80
83
|
let token;
|
|
@@ -96,7 +99,7 @@ class KeycloakService extends Keycloak{
|
|
|
96
99
|
grant_type: keycloakConfig.GRANT_TYPE,
|
|
97
100
|
},
|
|
98
101
|
};
|
|
99
|
-
|
|
102
|
+
|
|
100
103
|
try {
|
|
101
104
|
// T.O.K.E.N R.E.Q.U.E.S.T # 1 ( P.E.R.M.I.S.S.I.O.N.S N.O.T I.N.C.L.U.D.E.D)
|
|
102
105
|
let tokenResponse = await requestController.httpRequest(config, true);
|
|
@@ -116,11 +119,12 @@ class KeycloakService extends Keycloak{
|
|
|
116
119
|
config.data.token = token;
|
|
117
120
|
URL = URL + '/introspect'
|
|
118
121
|
config.url = URL;
|
|
119
|
-
// T.O.K.E.N R.E.Q.U.E.S.T # 3 (A.C.C.E.S.S T.O.K.E.N I.N.T.R.O.S.P.E.C.T.I.O.N)
|
|
120
122
|
|
|
123
|
+
// T.O.K.E.N R.E.Q.U.E.S.T # 3 (A.C.C.E.S.S T.O.K.E.N I.N.T.R.O.S.P.E.C.T.I.O.N)
|
|
121
124
|
try {
|
|
122
125
|
let intrsopectionResponse = await requestController.httpRequest(config, true);
|
|
123
126
|
intrsopectionResponse.data.access_token = token;
|
|
127
|
+
|
|
124
128
|
// T.O.K.E.N R.E.Q.U.E.S.T # 4 ( A.D.M.I.N. T.O.K.E.N)
|
|
125
129
|
try {
|
|
126
130
|
config.data.username = keycloakConfig.USERNAME_ADMIN;
|
|
@@ -152,16 +156,29 @@ class KeycloakService extends Keycloak{
|
|
|
152
156
|
};
|
|
153
157
|
|
|
154
158
|
//Adding user custom attribute to our token object data.
|
|
155
|
-
if(getuserDetails.data[0].attributes){
|
|
159
|
+
if (getuserDetails.data[0].attributes) {
|
|
156
160
|
responseObject.attributes = getuserDetails.data[0].attributes;
|
|
157
161
|
}
|
|
158
162
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
'keycloak_User': responseObject
|
|
162
|
-
}
|
|
163
|
-
resolve(finalObject);
|
|
163
|
+
delete config.headers.Authorization;
|
|
164
|
+
delete config.data;
|
|
164
165
|
|
|
166
|
+
//Fetching Groups data for each user.
|
|
167
|
+
try {
|
|
168
|
+
|
|
169
|
+
let teamData = await this.getUserSupervisedGroups(responseObject.id, responseObject.username);
|
|
170
|
+
responseObject.userTeam = teamData.userTeam;
|
|
171
|
+
responseObject.supervisedTeams = teamData.supervisedTeams;
|
|
172
|
+
|
|
173
|
+
let finalObject = {
|
|
174
|
+
'token': userToken,
|
|
175
|
+
'keycloak_User': responseObject
|
|
176
|
+
}
|
|
177
|
+
resolve(finalObject);
|
|
178
|
+
|
|
179
|
+
} catch (error) {
|
|
180
|
+
reject("Error while fetching Groups of User in Auth Process" + error);
|
|
181
|
+
}
|
|
165
182
|
}
|
|
166
183
|
catch (error) {
|
|
167
184
|
reject("Get all users request not sent" + error);
|
|
@@ -196,6 +213,48 @@ class KeycloakService extends Keycloak{
|
|
|
196
213
|
});
|
|
197
214
|
}
|
|
198
215
|
|
|
216
|
+
async getUserInfoFromToken(username, token) {
|
|
217
|
+
|
|
218
|
+
return new Promise(async (resolve, reject) => {
|
|
219
|
+
|
|
220
|
+
var URL = keycloakConfig["auth-server-url"] + 'realms/' + keycloakConfig.realm + '/protocol/openid-connect/token/introspect';
|
|
221
|
+
|
|
222
|
+
let config = {
|
|
223
|
+
method: 'post',
|
|
224
|
+
url: URL,
|
|
225
|
+
headers: {
|
|
226
|
+
'Accept': 'application/json',
|
|
227
|
+
'cache-control': 'no-cache',
|
|
228
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
229
|
+
},
|
|
230
|
+
data: {
|
|
231
|
+
username: username,
|
|
232
|
+
client_id: keycloakConfig.CLIENT_ID,
|
|
233
|
+
client_secret: keycloakConfig.credentials.secret,
|
|
234
|
+
token: token
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
|
|
240
|
+
let userInfo = await requestController.httpRequest(config, true);
|
|
241
|
+
if (!(userInfo.data.active)) {
|
|
242
|
+
resolve({
|
|
243
|
+
'status': 401,
|
|
244
|
+
'message': 'Provided User Access Token is Expired.'
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
let clientRoles = userInfo.data.resource_access;
|
|
249
|
+
resolve(clientRoles);
|
|
250
|
+
|
|
251
|
+
} catch (error) {
|
|
252
|
+
reject(error);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
}
|
|
257
|
+
|
|
199
258
|
createResource(resource_name, resource_scope = keycloakConfig.SCOPE_NAME) {
|
|
200
259
|
return new Promise(async (resolve, reject) => {
|
|
201
260
|
|
|
@@ -369,7 +428,7 @@ class KeycloakService extends Keycloak{
|
|
|
369
428
|
config.data = JSON.stringify(config.data);
|
|
370
429
|
|
|
371
430
|
try {
|
|
372
|
-
let policyResponse = await requestController.httpRequest(config,false);
|
|
431
|
+
let policyResponse = await requestController.httpRequest(config, false);
|
|
373
432
|
resolve(policyResponse);
|
|
374
433
|
} catch (error) {
|
|
375
434
|
reject("Policy error" + error);
|
|
@@ -383,7 +442,7 @@ class KeycloakService extends Keycloak{
|
|
|
383
442
|
|
|
384
443
|
}
|
|
385
444
|
|
|
386
|
-
createPermission(resourceName, policyName, permissionName, scopeName){
|
|
445
|
+
createPermission(resourceName, policyName, permissionName, scopeName) {
|
|
387
446
|
|
|
388
447
|
return new Promise(async (resolve, reject) => {
|
|
389
448
|
let token;
|
|
@@ -433,7 +492,7 @@ class KeycloakService extends Keycloak{
|
|
|
433
492
|
config.data = JSON.stringify(config.data);
|
|
434
493
|
|
|
435
494
|
try {
|
|
436
|
-
let policyResponse = await requestController.httpRequest(config,false);
|
|
495
|
+
let policyResponse = await requestController.httpRequest(config, false);
|
|
437
496
|
resolve(policyResponse);
|
|
438
497
|
} catch (error) {
|
|
439
498
|
reject("Policy error" + error);
|
|
@@ -503,7 +562,7 @@ class KeycloakService extends Keycloak{
|
|
|
503
562
|
});
|
|
504
563
|
}
|
|
505
564
|
|
|
506
|
-
revokeUseronResource(resource_name, keycloak_user_id){
|
|
565
|
+
revokeUseronResource(resource_name, keycloak_user_id) {
|
|
507
566
|
return new Promise(async (resolve, reject) => {
|
|
508
567
|
let token;
|
|
509
568
|
try {
|
|
@@ -558,6 +617,290 @@ class KeycloakService extends Keycloak{
|
|
|
558
617
|
});
|
|
559
618
|
}
|
|
560
619
|
|
|
620
|
+
//function to be used only in teams implementation
|
|
621
|
+
async getUserSupervisedGroups(userId, username) {
|
|
622
|
+
|
|
623
|
+
return new Promise(async (resolve, reject) => {
|
|
624
|
+
|
|
625
|
+
let error;
|
|
626
|
+
let token;
|
|
627
|
+
var URL = keycloakConfig["auth-server-url"] + 'realms/' + keycloakConfig.realm + '/protocol/openid-connect/token';
|
|
628
|
+
|
|
629
|
+
try {
|
|
630
|
+
|
|
631
|
+
var config = {
|
|
632
|
+
method: 'post',
|
|
633
|
+
url: URL,
|
|
634
|
+
headers: {
|
|
635
|
+
'Accept': 'application/json',
|
|
636
|
+
'cache-control': 'no-cache',
|
|
637
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
638
|
+
},
|
|
639
|
+
data: {
|
|
640
|
+
client_id: keycloakConfig.CLIENT_ID,
|
|
641
|
+
username: keycloakConfig.USERNAME_ADMIN,
|
|
642
|
+
password: keycloakConfig.PASSWORD_ADMIN,
|
|
643
|
+
grant_type: keycloakConfig.GRANT_TYPE,
|
|
644
|
+
client_secret: keycloakConfig.credentials.secret
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
try {
|
|
649
|
+
|
|
650
|
+
let adminTokenResponse = await requestController.httpRequest(config, true);
|
|
651
|
+
token = adminTokenResponse.data.access_token;
|
|
652
|
+
|
|
653
|
+
config.method = 'get';
|
|
654
|
+
delete config.data;
|
|
655
|
+
delete config.url;
|
|
656
|
+
|
|
657
|
+
let URL2 = keycloakConfig["auth-server-url"] + 'admin/realms/' + keycloakConfig.realm + '/users/' + userId + '/groups';
|
|
658
|
+
config.url = URL2;
|
|
659
|
+
config.headers.Authorization = 'Bearer ' + token;
|
|
660
|
+
|
|
661
|
+
try {
|
|
662
|
+
|
|
663
|
+
let userGroup = await requestController.httpRequest(config, true);
|
|
664
|
+
let team = {};
|
|
665
|
+
|
|
666
|
+
if (userGroup.data.length != 0) {
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
let group = userGroup.data;
|
|
670
|
+
let userTeam = {};
|
|
671
|
+
let supervisedTeams = [];
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
userTeam = {
|
|
675
|
+
'teamId': group[0].id,
|
|
676
|
+
'teamName': group[0].name
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
team.userTeam = userTeam;
|
|
680
|
+
|
|
681
|
+
config.method = 'get';
|
|
682
|
+
delete config.data;
|
|
683
|
+
delete config.url;
|
|
684
|
+
|
|
685
|
+
let URL3 = keycloakConfig["auth-server-url"] + 'admin/realms/' + keycloakConfig.realm + '/groups';
|
|
686
|
+
config.url = URL3;
|
|
687
|
+
config.headers.Authorization = 'Bearer ' + token;
|
|
688
|
+
|
|
689
|
+
try {
|
|
690
|
+
|
|
691
|
+
let allGroups = await requestController.httpRequest(config, true);
|
|
692
|
+
|
|
693
|
+
for (let group of allGroups.data) {
|
|
694
|
+
|
|
695
|
+
let result = await teamsService.getGroupByGroupID(group.id, username, token, keycloakConfig);
|
|
696
|
+
|
|
697
|
+
if (result) {
|
|
698
|
+
supervisedTeams.push(result);
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
team.supervisedTeams = supervisedTeams;
|
|
703
|
+
resolve(team);
|
|
704
|
+
|
|
705
|
+
} catch (er) {
|
|
706
|
+
|
|
707
|
+
error = await this.checkErrorType(er);
|
|
708
|
+
reject(error);
|
|
709
|
+
}
|
|
710
|
+
} else {
|
|
711
|
+
|
|
712
|
+
team.userTeam = {};
|
|
713
|
+
team.supervisedTeams = [];
|
|
714
|
+
|
|
715
|
+
resolve(team);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
} catch (er) {
|
|
719
|
+
|
|
720
|
+
error = await this.checkErrorType(er);
|
|
721
|
+
reject(error);
|
|
722
|
+
}
|
|
723
|
+
} catch (er) {
|
|
724
|
+
|
|
725
|
+
error = await this.checkErrorType(er);
|
|
726
|
+
reject(error);
|
|
727
|
+
}
|
|
728
|
+
} catch (er) {
|
|
729
|
+
|
|
730
|
+
error = await this.checkErrorType(er);
|
|
731
|
+
reject(error);
|
|
732
|
+
};
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
//function to be used only in teams implementation
|
|
738
|
+
async getTeamUsers(keycloakObj, groupsIdsArr, userToken) {
|
|
739
|
+
|
|
740
|
+
return new Promise(async (resolve, reject) => {
|
|
741
|
+
|
|
742
|
+
let token;
|
|
743
|
+
let message;
|
|
744
|
+
var URL = keycloakConfig["auth-server-url"] + 'realms/' + keycloakConfig.realm + '/protocol/openid-connect/token';
|
|
745
|
+
|
|
746
|
+
try {
|
|
747
|
+
|
|
748
|
+
if (typeof keycloakObj == 'object' && Object.keys(keycloakObj).length != 0
|
|
749
|
+
&& Array.isArray(groupsIdsArr) && userToken.length > 0) {
|
|
750
|
+
|
|
751
|
+
//Validate whether user in keycloakObj is same as user in userToken.
|
|
752
|
+
const parseJwt = (userToken) => {
|
|
753
|
+
try {
|
|
754
|
+
return JSON.parse(Buffer.from(userToken.split('.')[1], 'base64').toString());
|
|
755
|
+
} catch (er) {
|
|
756
|
+
return null;
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
let verifyToken = parseJwt(userToken);
|
|
761
|
+
|
|
762
|
+
if (keycloakObj.username != verifyToken.preferred_username) {
|
|
763
|
+
|
|
764
|
+
message = `The data provided in Keycloak Object as an Argument doesn't belong to current Logged-In user.`;
|
|
765
|
+
|
|
766
|
+
resolve({
|
|
767
|
+
"status": 401,
|
|
768
|
+
"message": message
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
var config = {
|
|
773
|
+
method: 'post',
|
|
774
|
+
url: URL,
|
|
775
|
+
headers: {
|
|
776
|
+
'Accept': 'application/json',
|
|
777
|
+
'cache-control': 'no-cache',
|
|
778
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
779
|
+
},
|
|
780
|
+
data: {
|
|
781
|
+
client_id: keycloakConfig.CLIENT_ID,
|
|
782
|
+
username: keycloakConfig.USERNAME_ADMIN,
|
|
783
|
+
password: keycloakConfig.PASSWORD_ADMIN,
|
|
784
|
+
grant_type: keycloakConfig.GRANT_TYPE,
|
|
785
|
+
client_secret: keycloakConfig.credentials.secret
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
try {
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
let adminTokenResponse = await requestController.httpRequest(config, true);
|
|
794
|
+
token = adminTokenResponse.data.access_token;
|
|
795
|
+
let allUsers = [];
|
|
796
|
+
let groupsData;
|
|
797
|
+
|
|
798
|
+
config.method = 'get';
|
|
799
|
+
delete config.data;
|
|
800
|
+
delete config.url;
|
|
801
|
+
config.headers.Authorization = 'Bearer ' + token;
|
|
802
|
+
let clientRoles = await this.getUserInfoFromToken(keycloakObj.username, userToken);
|
|
803
|
+
|
|
804
|
+
if (clientRoles.status) {
|
|
805
|
+
resolve(clientRoles);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
//admin case
|
|
809
|
+
if ('realm-management' in clientRoles) {
|
|
810
|
+
|
|
811
|
+
let URL2 = keycloakConfig["auth-server-url"] + 'admin/realms/' + keycloakConfig.realm + '/groups';
|
|
812
|
+
config.url = URL2;
|
|
813
|
+
|
|
814
|
+
try {
|
|
815
|
+
|
|
816
|
+
let groups = await requestController.httpRequest(config, true);
|
|
817
|
+
delete config.url
|
|
818
|
+
|
|
819
|
+
groupsData = groups.data;
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
} catch (er) {
|
|
823
|
+
|
|
824
|
+
let error = await this.checkErrorType(er);
|
|
825
|
+
reject(error);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
} else {
|
|
829
|
+
|
|
830
|
+
//agent case
|
|
831
|
+
if ((keycloakObj.supervisedTeams).length == 0) {
|
|
832
|
+
|
|
833
|
+
resolve([]);
|
|
834
|
+
|
|
835
|
+
//supervisor case
|
|
836
|
+
} else {
|
|
837
|
+
|
|
838
|
+
//if no group ids are provided, send all the users of groups this user supervises.
|
|
839
|
+
if (groupsIdsArr.length == 0) {
|
|
840
|
+
|
|
841
|
+
let supervisedGroups = keycloakObj.supervisedTeams;
|
|
842
|
+
groupsData = supervisedGroups;
|
|
843
|
+
|
|
844
|
+
//only send the users of provided groups.
|
|
845
|
+
} else {
|
|
846
|
+
|
|
847
|
+
let groupsArr = [];
|
|
848
|
+
let idsArr = groupsIdsArr;
|
|
849
|
+
|
|
850
|
+
idsArr.forEach(id => {
|
|
851
|
+
let group = keycloakObj.supervisedTeams.find(group => {
|
|
852
|
+
return group.teamId == id;
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
if (!group) {
|
|
856
|
+
|
|
857
|
+
message = `Given User doesn't suprvise any group against id: ${id}`;
|
|
858
|
+
|
|
859
|
+
resolve({
|
|
860
|
+
"status": 404,
|
|
861
|
+
"message": message
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
groupsArr.push(group);
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
groupsData = groupsArr;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
allUsers = await teamsService.getUsersOfGroups(groupsData, config, keycloakConfig);
|
|
874
|
+
resolve(allUsers);
|
|
875
|
+
|
|
876
|
+
} catch (er) {
|
|
877
|
+
console.log(er);
|
|
878
|
+
let error = await this.checkErrorType(er);
|
|
879
|
+
console.log(error);
|
|
880
|
+
reject(error);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
message = 'Please pass the valid arguments. First argument must be (should not be empty object,' +
|
|
887
|
+
'must contain valid key-value pair) and Second argument must be Array of groupIds (could be an empty array)' +
|
|
888
|
+
'3rd Argument must be valid Access Token of current logged-in user.';
|
|
889
|
+
|
|
890
|
+
resolve({
|
|
891
|
+
"status": 400,
|
|
892
|
+
"message": message
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
} catch (er) {
|
|
896
|
+
|
|
897
|
+
let error = await this.checkErrorType(er);
|
|
898
|
+
reject(error);
|
|
899
|
+
};
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
}
|
|
903
|
+
|
|
561
904
|
// this function requires comma separated list of roles in parameter e.g ["robot","human","customer"];
|
|
562
905
|
getUsersByRole(keycloak_roles) {
|
|
563
906
|
return new Promise(async (resolve, reject) => {
|
|
@@ -600,30 +943,30 @@ class KeycloakService extends Keycloak{
|
|
|
600
943
|
|
|
601
944
|
userObject.forEach((user) => {
|
|
602
945
|
|
|
603
|
-
if(count > 0){
|
|
946
|
+
if (count > 0) {
|
|
604
947
|
|
|
605
|
-
|
|
948
|
+
let userIndex = obj.findIndex(usr => {
|
|
606
949
|
return usr.username == user.username;
|
|
607
|
-
|
|
950
|
+
});
|
|
608
951
|
|
|
609
|
-
|
|
952
|
+
if (userIndex != -1) {
|
|
610
953
|
obj[userIndex].roles.push(keycloak_roles[i]);
|
|
611
954
|
flag = false;
|
|
612
|
-
|
|
955
|
+
}
|
|
613
956
|
}
|
|
614
|
-
|
|
615
|
-
if(flag == true){
|
|
957
|
+
|
|
958
|
+
if (flag == true) {
|
|
616
959
|
|
|
617
960
|
obj.push({
|
|
618
961
|
'id': user.id,
|
|
619
962
|
'username': user.username,
|
|
620
|
-
'firstName': ((user.firstName == undefined)? "" : user.firstName),
|
|
621
|
-
'lastName': ((user.lastName == undefined)? "" : user.lastName),
|
|
963
|
+
'firstName': ((user.firstName == undefined) ? "" : user.firstName),
|
|
964
|
+
'lastName': ((user.lastName == undefined) ? "" : user.lastName),
|
|
622
965
|
'roles': [keycloak_roles[i]]
|
|
623
|
-
})
|
|
966
|
+
})
|
|
624
967
|
|
|
625
968
|
}
|
|
626
|
-
|
|
969
|
+
|
|
627
970
|
flag = true;
|
|
628
971
|
|
|
629
972
|
});
|
|
@@ -647,14 +990,14 @@ class KeycloakService extends Keycloak{
|
|
|
647
990
|
});
|
|
648
991
|
}
|
|
649
992
|
|
|
650
|
-
|
|
651
|
-
async getRealmRoles(adminToken){
|
|
993
|
+
|
|
994
|
+
async getRealmRoles(adminToken) {
|
|
652
995
|
|
|
653
996
|
return new Promise(async (resolve, reject) => {
|
|
654
|
-
|
|
997
|
+
|
|
655
998
|
let URL = `${keycloakConfig["auth-server-url"]}${keycloakConfig["USERNAME_ADMIN"]}/realms/${keycloakConfig["realm"]}/roles`;
|
|
656
999
|
|
|
657
|
-
|
|
1000
|
+
|
|
658
1001
|
|
|
659
1002
|
let config = {
|
|
660
1003
|
method: 'get',
|
|
@@ -680,12 +1023,12 @@ class KeycloakService extends Keycloak{
|
|
|
680
1023
|
});
|
|
681
1024
|
}
|
|
682
1025
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
async assignRoleToUser(userId, roles, adminToken){
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
async assignRoleToUser(userId, roles, adminToken) {
|
|
686
1029
|
|
|
687
1030
|
return new Promise(async (resolve, reject) => {
|
|
688
|
-
|
|
1031
|
+
|
|
689
1032
|
let URL = `${keycloakConfig["auth-server-url"]}${keycloakConfig["USERNAME_ADMIN"]}/realms/${keycloakConfig["realm"]}/users/${userId}/role-mappings/realm`;
|
|
690
1033
|
|
|
691
1034
|
let config = {
|
|
@@ -713,14 +1056,14 @@ class KeycloakService extends Keycloak{
|
|
|
713
1056
|
|
|
714
1057
|
});
|
|
715
1058
|
}
|
|
716
|
-
|
|
717
1059
|
|
|
718
|
-
|
|
1060
|
+
|
|
1061
|
+
async createUser(username, password, token, userRoles) {
|
|
719
1062
|
|
|
720
1063
|
let assignRole = [];
|
|
721
1064
|
|
|
722
1065
|
return new Promise(async (resolve, reject) => {
|
|
723
|
-
|
|
1066
|
+
|
|
724
1067
|
let URL = `${keycloakConfig["auth-server-url"]}${keycloakConfig["USERNAME_ADMIN"]}/realms/${keycloakConfig["realm"]}/users`;
|
|
725
1068
|
|
|
726
1069
|
let data = {
|
|
@@ -735,7 +1078,7 @@ class KeycloakService extends Keycloak{
|
|
|
735
1078
|
]
|
|
736
1079
|
}
|
|
737
1080
|
|
|
738
|
-
|
|
1081
|
+
|
|
739
1082
|
|
|
740
1083
|
let config = {
|
|
741
1084
|
method: 'post',
|
|
@@ -751,22 +1094,22 @@ class KeycloakService extends Keycloak{
|
|
|
751
1094
|
|
|
752
1095
|
let tokenResponse = await requestController.httpRequest(config, false);
|
|
753
1096
|
|
|
754
|
-
if(userRoles != []){
|
|
1097
|
+
if (userRoles != []) {
|
|
755
1098
|
//Get the user id at time of creation
|
|
756
1099
|
let userLocation = tokenResponse.headers.location;
|
|
757
1100
|
let userLocationSplit = userLocation.split("/");
|
|
758
1101
|
let userId = userLocationSplit[(userLocationSplit.length) - 1];
|
|
759
1102
|
|
|
760
|
-
|
|
1103
|
+
|
|
761
1104
|
//Get list of all the roles in keycloak realm
|
|
762
1105
|
let realmRoles = await this.getRealmRoles(token);
|
|
763
|
-
|
|
1106
|
+
|
|
764
1107
|
//checking whether role exist in realmRoles object array:
|
|
765
|
-
for(let role of realmRoles.data){
|
|
766
|
-
|
|
1108
|
+
for (let role of realmRoles.data) {
|
|
1109
|
+
|
|
767
1110
|
userRoles.forEach(userRole => {
|
|
768
1111
|
|
|
769
|
-
if(role.name == userRole.toLocaleLowerCase()){
|
|
1112
|
+
if (role.name == userRole.toLocaleLowerCase()) {
|
|
770
1113
|
assignRole.push({
|
|
771
1114
|
id: role.id,
|
|
772
1115
|
name: role.name
|
|
@@ -780,16 +1123,16 @@ class KeycloakService extends Keycloak{
|
|
|
780
1123
|
let roleAssigned = await this.assignRoleToUser(userId, assignRole, token);
|
|
781
1124
|
|
|
782
1125
|
//Role assigned with status
|
|
783
|
-
if(roleAssigned.status == 204){
|
|
1126
|
+
if (roleAssigned.status == 204) {
|
|
784
1127
|
resolve(tokenResponse);
|
|
785
1128
|
}
|
|
786
1129
|
|
|
787
|
-
}else{
|
|
1130
|
+
} else {
|
|
788
1131
|
|
|
789
1132
|
resolve(tokenResponse);
|
|
790
1133
|
|
|
791
1134
|
}
|
|
792
|
-
|
|
1135
|
+
|
|
793
1136
|
|
|
794
1137
|
}
|
|
795
1138
|
catch (err) {
|
|
@@ -803,132 +1146,150 @@ class KeycloakService extends Keycloak{
|
|
|
803
1146
|
}
|
|
804
1147
|
|
|
805
1148
|
//Authenticating Finesse User
|
|
806
|
-
async authenticateFinesse(username,password, finesseUrl, userRoles, finesseToken){
|
|
807
|
-
|
|
1149
|
+
async authenticateFinesse(username, password, finesseUrl, userRoles, finesseToken) {
|
|
1150
|
+
|
|
808
1151
|
//Authentication of Finesse User, it returns a status code 200 if user found and 401 if unauthorized.
|
|
809
1152
|
let finesseLoginResponse;
|
|
810
1153
|
|
|
811
|
-
if(finesseToken.length == 0){
|
|
812
|
-
finesseLoginResponse = await finesseService.authenticateUserViaFinesse(username,password,finesseUrl);
|
|
1154
|
+
if (finesseToken.length == 0) {
|
|
1155
|
+
finesseLoginResponse = await finesseService.authenticateUserViaFinesse(username, password, finesseUrl);
|
|
813
1156
|
|
|
814
|
-
}else{
|
|
815
|
-
finesseLoginResponse = await finesseService.authenticateUserViaFinesseSSO(username,finesseToken,finesseUrl);
|
|
1157
|
+
} else {
|
|
1158
|
+
finesseLoginResponse = await finesseService.authenticateUserViaFinesseSSO(username, finesseToken, finesseUrl);
|
|
816
1159
|
}
|
|
817
1160
|
|
|
818
1161
|
//If user is SSO then password is not provided, we are setting up a pre-defined password.
|
|
819
|
-
password = (password.length == 0)?"123456":password;
|
|
820
|
-
|
|
1162
|
+
password = (password.length == 0) ? "123456" : password;
|
|
1163
|
+
|
|
821
1164
|
let authenticatedByKeycloak = false;
|
|
822
1165
|
let keycloakAuthToken = null;
|
|
823
1166
|
let timeoutErr = null;
|
|
824
1167
|
|
|
825
|
-
if(finesseLoginResponse.status == 200){
|
|
826
|
-
try{
|
|
827
|
-
|
|
1168
|
+
if (finesseLoginResponse.status == 200) {
|
|
1169
|
+
try {
|
|
1170
|
+
|
|
828
1171
|
//Checking whether finesse user already exist in keycloak and fetch its token
|
|
829
|
-
keycloakAuthToken = await this.getKeycloakTokenWithIntrospect(username,password,keycloakConfig["realm"]);
|
|
1172
|
+
keycloakAuthToken = await this.getKeycloakTokenWithIntrospect(username, password, keycloakConfig["realm"]);
|
|
830
1173
|
authenticatedByKeycloak = true;
|
|
831
|
-
|
|
832
|
-
}catch(err){
|
|
833
1174
|
|
|
834
|
-
|
|
1175
|
+
} catch (err) {
|
|
1176
|
+
|
|
1177
|
+
if (err.response.status == 401) {
|
|
835
1178
|
|
|
836
1179
|
console.log("User doesn't exist in Keycloak, syncing finesse user in keycloak...");
|
|
837
1180
|
|
|
838
|
-
}else{
|
|
1181
|
+
} else {
|
|
839
1182
|
|
|
840
|
-
throw({
|
|
1183
|
+
throw ({
|
|
841
1184
|
"status": err.response.status,
|
|
842
1185
|
"message": err.response.data.error_description
|
|
843
1186
|
});
|
|
844
1187
|
|
|
845
1188
|
}
|
|
846
1189
|
|
|
847
|
-
}finally{
|
|
848
|
-
|
|
849
|
-
//Finesse User not found in keycloak, so we are going to create one.
|
|
850
|
-
if(!authenticatedByKeycloak){
|
|
1190
|
+
} finally {
|
|
851
1191
|
|
|
852
|
-
|
|
1192
|
+
//Finesse User not found in keycloak, so we are going to create one.
|
|
1193
|
+
if (!authenticatedByKeycloak) {
|
|
853
1194
|
|
|
854
|
-
|
|
855
|
-
keycloakAuthToken = await this.getKeycloakTokenWithIntrospect(keycloakConfig["USERNAME_ADMIN"],keycloakConfig["PASSWORD_ADMIN"],keycloakConfig["realm"]);
|
|
856
|
-
}catch(err){
|
|
1195
|
+
try {
|
|
857
1196
|
|
|
858
|
-
|
|
1197
|
+
//Fetching admin token, we pass it in our "Create User" API for authorization
|
|
1198
|
+
keycloakAuthToken = await this.getKeycloakTokenWithIntrospect(keycloakConfig["USERNAME_ADMIN"], keycloakConfig["PASSWORD_ADMIN"], keycloakConfig["realm"]);
|
|
1199
|
+
} catch (err) {
|
|
859
1200
|
|
|
860
|
-
|
|
861
|
-
'Keycloak login status': 408,
|
|
862
|
-
'keycloak login message': `Keycloak server unaccessable against URL: ${keycloakConfig["auth-server-url"]}`
|
|
863
|
-
});
|
|
1201
|
+
if (err.code == "ETIMEDOUT") {
|
|
864
1202
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
});
|
|
870
|
-
}
|
|
1203
|
+
throw ({
|
|
1204
|
+
'Keycloak login status': 408,
|
|
1205
|
+
'keycloak login message': `Keycloak server unaccessable against URL: ${keycloakConfig["auth-server-url"]}`
|
|
1206
|
+
});
|
|
871
1207
|
|
|
1208
|
+
} else {
|
|
1209
|
+
throw ({
|
|
1210
|
+
"status": err.response.status,
|
|
1211
|
+
"message": "Error While getting Keycloak admin token: " + err.response.data.error_description
|
|
1212
|
+
});
|
|
872
1213
|
}
|
|
873
1214
|
|
|
874
|
-
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
if (keycloakAuthToken.token) {
|
|
875
1218
|
|
|
876
|
-
|
|
1219
|
+
let token = keycloakAuthToken.token;
|
|
877
1220
|
|
|
878
|
-
|
|
879
|
-
|
|
1221
|
+
//validating customer Before Creation
|
|
1222
|
+
let { error, value } = validateUser({ username, password, token, userRoles });
|
|
880
1223
|
|
|
881
|
-
|
|
1224
|
+
if (error) {
|
|
882
1225
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
}
|
|
1226
|
+
throw ({
|
|
1227
|
+
"status": 400,
|
|
1228
|
+
"message": "Error while creation of user, error message: " + error.details[0].message
|
|
1229
|
+
})
|
|
888
1230
|
}
|
|
1231
|
+
}
|
|
889
1232
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
if(userCreated.status == 201){
|
|
1233
|
+
try {
|
|
1234
|
+
//Creating Finesse User inside keycloak.
|
|
1235
|
+
let userCreated = await this.createUser(username, password, keycloakAuthToken.token, userRoles);
|
|
895
1236
|
|
|
896
|
-
|
|
897
|
-
keycloakAuthToken = await this.getKeycloakTokenWithIntrospect(username,password,keycloakConfig["realm"]);
|
|
898
|
-
}
|
|
899
|
-
|
|
1237
|
+
if (userCreated.status == 201) {
|
|
900
1238
|
|
|
901
|
-
|
|
1239
|
+
//Returning the token of recently created User
|
|
1240
|
+
keycloakAuthToken = await this.getKeycloakTokenWithIntrospect(username, password, keycloakConfig["realm"]);
|
|
1241
|
+
}
|
|
902
1242
|
|
|
903
|
-
if(err.code == "ETIMEDOUT"){
|
|
904
1243
|
|
|
905
|
-
|
|
906
|
-
'Keycloak login status': 408,
|
|
907
|
-
'keycloak login message': `Keycloak server unaccessable against URL: ${keycloakConfig["auth-server-url"]}`
|
|
908
|
-
});
|
|
1244
|
+
} catch (err) {
|
|
909
1245
|
|
|
910
|
-
|
|
1246
|
+
if (err.code == "ETIMEDOUT") {
|
|
911
1247
|
|
|
912
|
-
|
|
1248
|
+
throw ({
|
|
1249
|
+
'Keycloak login status': 408,
|
|
1250
|
+
'keycloak login message': `Keycloak server unaccessable against URL: ${keycloakConfig["auth-server-url"]}`
|
|
1251
|
+
});
|
|
913
1252
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1253
|
+
} else {
|
|
1254
|
+
|
|
1255
|
+
console.log(err);
|
|
1256
|
+
|
|
1257
|
+
throw ({
|
|
1258
|
+
"status": err.response.status,
|
|
1259
|
+
"message": "Error While creating Keycloak user: " + err.response.data.error_description
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
919
1262
|
}
|
|
920
1263
|
}
|
|
921
1264
|
}
|
|
922
|
-
|
|
1265
|
+
|
|
923
1266
|
return keycloakAuthToken;
|
|
924
1267
|
|
|
925
|
-
}else{
|
|
1268
|
+
} else {
|
|
926
1269
|
|
|
927
1270
|
return finesseLoginResponse
|
|
928
1271
|
|
|
929
1272
|
}
|
|
930
1273
|
}
|
|
931
1274
|
|
|
1275
|
+
async checkErrorType(err) {
|
|
1276
|
+
|
|
1277
|
+
if (err.code == "ETIMEDOUT") {
|
|
1278
|
+
|
|
1279
|
+
return ({
|
|
1280
|
+
'Keycloak login status': 408,
|
|
1281
|
+
'keycloak login message': `Keycloak server unaccessable against URL: ${keycloakConfig["auth-server-url"]}`
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
} else {
|
|
1285
|
+
|
|
1286
|
+
return ({
|
|
1287
|
+
"status": err.response.status,
|
|
1288
|
+
"message": "Error: " + err.response.data.error_description
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
932
1293
|
}
|
|
933
1294
|
|
|
934
1295
|
function validateUser(userData) {
|
|
@@ -938,7 +1299,7 @@ function validateUser(userData) {
|
|
|
938
1299
|
token: Joi.string().required(),
|
|
939
1300
|
userRoles: Joi.array().items(Joi.string()).allow(null)
|
|
940
1301
|
});
|
|
941
|
-
|
|
1302
|
+
|
|
942
1303
|
return schema.validate(userData);
|
|
943
1304
|
}
|
|
944
1305
|
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
let requestController = require("../controller/requestController.js");
|
|
2
|
+
|
|
3
|
+
class TeamsService {
|
|
4
|
+
|
|
5
|
+
constructor() {
|
|
6
|
+
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//This function is being used inside getUserSupervisedGroups()
|
|
10
|
+
async getGroupByGroupID(groupId, username, token, keycloakConfig) {
|
|
11
|
+
|
|
12
|
+
return new Promise(async (resolve, reject) => {
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
|
|
16
|
+
let URL = keycloakConfig["auth-server-url"] + 'admin/realms/' + keycloakConfig.realm + '/groups/' + groupId + '/';
|
|
17
|
+
|
|
18
|
+
var config = {
|
|
19
|
+
method: 'get',
|
|
20
|
+
url: URL,
|
|
21
|
+
headers: {
|
|
22
|
+
'Accept': 'application/json',
|
|
23
|
+
'cache-control': 'no-cache',
|
|
24
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
25
|
+
},
|
|
26
|
+
headers: {
|
|
27
|
+
'Authorization': 'Bearer ' + token
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
|
|
33
|
+
let groupAttributes = await requestController.httpRequest(config, false);
|
|
34
|
+
let attributes = groupAttributes.data.attributes;
|
|
35
|
+
|
|
36
|
+
if (attributes != null) {
|
|
37
|
+
|
|
38
|
+
if ('supervisor' in attributes) {
|
|
39
|
+
|
|
40
|
+
let supervisors = attributes['supervisor'][0].split(",");
|
|
41
|
+
|
|
42
|
+
if (supervisors.includes(username)) {
|
|
43
|
+
|
|
44
|
+
resolve({
|
|
45
|
+
'teamId': groupAttributes.data.id,
|
|
46
|
+
'teamName': groupAttributes.data.name
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
resolve(null);
|
|
53
|
+
|
|
54
|
+
} catch (error) {
|
|
55
|
+
reject(error);
|
|
56
|
+
}
|
|
57
|
+
} catch (er) {
|
|
58
|
+
reject("error" + er);
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//this function is being used inside getTeamUsers()
|
|
64
|
+
async getUsersOfGroups(groups, config, keycloakConfig) {
|
|
65
|
+
|
|
66
|
+
return new Promise(async (resolve, reject) => {
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
let allUsers = [];
|
|
70
|
+
let rolesArr = [];
|
|
71
|
+
|
|
72
|
+
if (groups.length > 0) {
|
|
73
|
+
|
|
74
|
+
for (let group of groups) {
|
|
75
|
+
|
|
76
|
+
let id = null;
|
|
77
|
+
let name = null;
|
|
78
|
+
|
|
79
|
+
if (typeof group === 'object') {
|
|
80
|
+
id = (group.id) ? group.id : group.teamId;
|
|
81
|
+
name = (group.name) ? group.name : group.teamName;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let URL = keycloakConfig["auth-server-url"] + 'admin/realms/' + keycloakConfig.realm + '/groups/' + id + '/members';
|
|
85
|
+
config.url = URL;
|
|
86
|
+
let users = await requestController.httpRequest(config, true);
|
|
87
|
+
|
|
88
|
+
if (users.data.length > 0) {
|
|
89
|
+
|
|
90
|
+
for (let user of users.data) {
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
|
|
94
|
+
let URL2 = keycloakConfig["auth-server-url"] + 'admin/realms/' + keycloakConfig.realm + '/users/' + user.id + '/role-mappings';
|
|
95
|
+
config.url = URL2;
|
|
96
|
+
let rolesObj = await requestController.httpRequest(config, true);
|
|
97
|
+
|
|
98
|
+
rolesArr = rolesObj.data.realmMappings.map(roles => {
|
|
99
|
+
return roles.name;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
} catch (er) {
|
|
103
|
+
reject(er);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let isExist = (allUsers).find(x => x.username == user.username);
|
|
107
|
+
|
|
108
|
+
if (!isExist) {
|
|
109
|
+
allUsers.push({
|
|
110
|
+
'id': user.id,
|
|
111
|
+
'username': user.username,
|
|
112
|
+
'firstName': (user.firstName) ? user.firstName : '',
|
|
113
|
+
'lastName': (user.lastName) ? user.lastName : '',
|
|
114
|
+
'realm': keycloakConfig.realm,
|
|
115
|
+
'roles': rolesArr,
|
|
116
|
+
'team': {
|
|
117
|
+
'teamId': id,
|
|
118
|
+
'teamName': name
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
resolve(allUsers);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
resolve([]);
|
|
133
|
+
|
|
134
|
+
} catch (er) {
|
|
135
|
+
reject(er);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
module.exports = TeamsService;
|