ef-keycloak-connect 1.2.3 → 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 +490 -123
- 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;
|
|
@@ -150,12 +154,31 @@ class KeycloakService extends Keycloak{
|
|
|
150
154
|
'realm': realm_name
|
|
151
155
|
|
|
152
156
|
};
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
157
|
+
|
|
158
|
+
//Adding user custom attribute to our token object data.
|
|
159
|
+
if (getuserDetails.data[0].attributes) {
|
|
160
|
+
responseObject.attributes = getuserDetails.data[0].attributes;
|
|
156
161
|
}
|
|
157
|
-
resolve(finalObject);
|
|
158
162
|
|
|
163
|
+
delete config.headers.Authorization;
|
|
164
|
+
delete config.data;
|
|
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
|
+
}
|
|
159
182
|
}
|
|
160
183
|
catch (error) {
|
|
161
184
|
reject("Get all users request not sent" + error);
|
|
@@ -190,6 +213,48 @@ class KeycloakService extends Keycloak{
|
|
|
190
213
|
});
|
|
191
214
|
}
|
|
192
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
|
+
|
|
193
258
|
createResource(resource_name, resource_scope = keycloakConfig.SCOPE_NAME) {
|
|
194
259
|
return new Promise(async (resolve, reject) => {
|
|
195
260
|
|
|
@@ -363,7 +428,7 @@ class KeycloakService extends Keycloak{
|
|
|
363
428
|
config.data = JSON.stringify(config.data);
|
|
364
429
|
|
|
365
430
|
try {
|
|
366
|
-
let policyResponse = await requestController.httpRequest(config,false);
|
|
431
|
+
let policyResponse = await requestController.httpRequest(config, false);
|
|
367
432
|
resolve(policyResponse);
|
|
368
433
|
} catch (error) {
|
|
369
434
|
reject("Policy error" + error);
|
|
@@ -377,7 +442,7 @@ class KeycloakService extends Keycloak{
|
|
|
377
442
|
|
|
378
443
|
}
|
|
379
444
|
|
|
380
|
-
createPermission(resourceName, policyName, permissionName, scopeName){
|
|
445
|
+
createPermission(resourceName, policyName, permissionName, scopeName) {
|
|
381
446
|
|
|
382
447
|
return new Promise(async (resolve, reject) => {
|
|
383
448
|
let token;
|
|
@@ -427,7 +492,7 @@ class KeycloakService extends Keycloak{
|
|
|
427
492
|
config.data = JSON.stringify(config.data);
|
|
428
493
|
|
|
429
494
|
try {
|
|
430
|
-
let policyResponse = await requestController.httpRequest(config,false);
|
|
495
|
+
let policyResponse = await requestController.httpRequest(config, false);
|
|
431
496
|
resolve(policyResponse);
|
|
432
497
|
} catch (error) {
|
|
433
498
|
reject("Policy error" + error);
|
|
@@ -497,7 +562,7 @@ class KeycloakService extends Keycloak{
|
|
|
497
562
|
});
|
|
498
563
|
}
|
|
499
564
|
|
|
500
|
-
revokeUseronResource(resource_name, keycloak_user_id){
|
|
565
|
+
revokeUseronResource(resource_name, keycloak_user_id) {
|
|
501
566
|
return new Promise(async (resolve, reject) => {
|
|
502
567
|
let token;
|
|
503
568
|
try {
|
|
@@ -552,6 +617,290 @@ class KeycloakService extends Keycloak{
|
|
|
552
617
|
});
|
|
553
618
|
}
|
|
554
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
|
+
|
|
555
904
|
// this function requires comma separated list of roles in parameter e.g ["robot","human","customer"];
|
|
556
905
|
getUsersByRole(keycloak_roles) {
|
|
557
906
|
return new Promise(async (resolve, reject) => {
|
|
@@ -594,30 +943,30 @@ class KeycloakService extends Keycloak{
|
|
|
594
943
|
|
|
595
944
|
userObject.forEach((user) => {
|
|
596
945
|
|
|
597
|
-
if(count > 0){
|
|
946
|
+
if (count > 0) {
|
|
598
947
|
|
|
599
|
-
|
|
948
|
+
let userIndex = obj.findIndex(usr => {
|
|
600
949
|
return usr.username == user.username;
|
|
601
|
-
|
|
950
|
+
});
|
|
602
951
|
|
|
603
|
-
|
|
952
|
+
if (userIndex != -1) {
|
|
604
953
|
obj[userIndex].roles.push(keycloak_roles[i]);
|
|
605
954
|
flag = false;
|
|
606
|
-
|
|
955
|
+
}
|
|
607
956
|
}
|
|
608
|
-
|
|
609
|
-
if(flag == true){
|
|
957
|
+
|
|
958
|
+
if (flag == true) {
|
|
610
959
|
|
|
611
960
|
obj.push({
|
|
612
961
|
'id': user.id,
|
|
613
962
|
'username': user.username,
|
|
614
|
-
'firstName': ((user.firstName == undefined)? "" : user.firstName),
|
|
615
|
-
'lastName': ((user.lastName == undefined)? "" : user.lastName),
|
|
963
|
+
'firstName': ((user.firstName == undefined) ? "" : user.firstName),
|
|
964
|
+
'lastName': ((user.lastName == undefined) ? "" : user.lastName),
|
|
616
965
|
'roles': [keycloak_roles[i]]
|
|
617
|
-
})
|
|
966
|
+
})
|
|
618
967
|
|
|
619
968
|
}
|
|
620
|
-
|
|
969
|
+
|
|
621
970
|
flag = true;
|
|
622
971
|
|
|
623
972
|
});
|
|
@@ -641,14 +990,14 @@ class KeycloakService extends Keycloak{
|
|
|
641
990
|
});
|
|
642
991
|
}
|
|
643
992
|
|
|
644
|
-
|
|
645
|
-
async getRealmRoles(adminToken){
|
|
993
|
+
|
|
994
|
+
async getRealmRoles(adminToken) {
|
|
646
995
|
|
|
647
996
|
return new Promise(async (resolve, reject) => {
|
|
648
|
-
|
|
997
|
+
|
|
649
998
|
let URL = `${keycloakConfig["auth-server-url"]}${keycloakConfig["USERNAME_ADMIN"]}/realms/${keycloakConfig["realm"]}/roles`;
|
|
650
999
|
|
|
651
|
-
|
|
1000
|
+
|
|
652
1001
|
|
|
653
1002
|
let config = {
|
|
654
1003
|
method: 'get',
|
|
@@ -674,12 +1023,12 @@ class KeycloakService extends Keycloak{
|
|
|
674
1023
|
});
|
|
675
1024
|
}
|
|
676
1025
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
async assignRoleToUser(userId, roles, adminToken){
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
async assignRoleToUser(userId, roles, adminToken) {
|
|
680
1029
|
|
|
681
1030
|
return new Promise(async (resolve, reject) => {
|
|
682
|
-
|
|
1031
|
+
|
|
683
1032
|
let URL = `${keycloakConfig["auth-server-url"]}${keycloakConfig["USERNAME_ADMIN"]}/realms/${keycloakConfig["realm"]}/users/${userId}/role-mappings/realm`;
|
|
684
1033
|
|
|
685
1034
|
let config = {
|
|
@@ -707,14 +1056,14 @@ class KeycloakService extends Keycloak{
|
|
|
707
1056
|
|
|
708
1057
|
});
|
|
709
1058
|
}
|
|
710
|
-
|
|
711
1059
|
|
|
712
|
-
|
|
1060
|
+
|
|
1061
|
+
async createUser(username, password, token, userRoles) {
|
|
713
1062
|
|
|
714
1063
|
let assignRole = [];
|
|
715
1064
|
|
|
716
1065
|
return new Promise(async (resolve, reject) => {
|
|
717
|
-
|
|
1066
|
+
|
|
718
1067
|
let URL = `${keycloakConfig["auth-server-url"]}${keycloakConfig["USERNAME_ADMIN"]}/realms/${keycloakConfig["realm"]}/users`;
|
|
719
1068
|
|
|
720
1069
|
let data = {
|
|
@@ -729,7 +1078,7 @@ class KeycloakService extends Keycloak{
|
|
|
729
1078
|
]
|
|
730
1079
|
}
|
|
731
1080
|
|
|
732
|
-
|
|
1081
|
+
|
|
733
1082
|
|
|
734
1083
|
let config = {
|
|
735
1084
|
method: 'post',
|
|
@@ -745,22 +1094,22 @@ class KeycloakService extends Keycloak{
|
|
|
745
1094
|
|
|
746
1095
|
let tokenResponse = await requestController.httpRequest(config, false);
|
|
747
1096
|
|
|
748
|
-
if(userRoles != []){
|
|
1097
|
+
if (userRoles != []) {
|
|
749
1098
|
//Get the user id at time of creation
|
|
750
1099
|
let userLocation = tokenResponse.headers.location;
|
|
751
1100
|
let userLocationSplit = userLocation.split("/");
|
|
752
1101
|
let userId = userLocationSplit[(userLocationSplit.length) - 1];
|
|
753
1102
|
|
|
754
|
-
|
|
1103
|
+
|
|
755
1104
|
//Get list of all the roles in keycloak realm
|
|
756
1105
|
let realmRoles = await this.getRealmRoles(token);
|
|
757
|
-
|
|
1106
|
+
|
|
758
1107
|
//checking whether role exist in realmRoles object array:
|
|
759
|
-
for(let role of realmRoles.data){
|
|
760
|
-
|
|
1108
|
+
for (let role of realmRoles.data) {
|
|
1109
|
+
|
|
761
1110
|
userRoles.forEach(userRole => {
|
|
762
1111
|
|
|
763
|
-
if(role.name == userRole.toLocaleLowerCase()){
|
|
1112
|
+
if (role.name == userRole.toLocaleLowerCase()) {
|
|
764
1113
|
assignRole.push({
|
|
765
1114
|
id: role.id,
|
|
766
1115
|
name: role.name
|
|
@@ -774,16 +1123,16 @@ class KeycloakService extends Keycloak{
|
|
|
774
1123
|
let roleAssigned = await this.assignRoleToUser(userId, assignRole, token);
|
|
775
1124
|
|
|
776
1125
|
//Role assigned with status
|
|
777
|
-
if(roleAssigned.status == 204){
|
|
1126
|
+
if (roleAssigned.status == 204) {
|
|
778
1127
|
resolve(tokenResponse);
|
|
779
1128
|
}
|
|
780
1129
|
|
|
781
|
-
}else{
|
|
1130
|
+
} else {
|
|
782
1131
|
|
|
783
1132
|
resolve(tokenResponse);
|
|
784
1133
|
|
|
785
1134
|
}
|
|
786
|
-
|
|
1135
|
+
|
|
787
1136
|
|
|
788
1137
|
}
|
|
789
1138
|
catch (err) {
|
|
@@ -797,132 +1146,150 @@ class KeycloakService extends Keycloak{
|
|
|
797
1146
|
}
|
|
798
1147
|
|
|
799
1148
|
//Authenticating Finesse User
|
|
800
|
-
async authenticateFinesse(username,password, finesseUrl, userRoles, finesseToken){
|
|
801
|
-
|
|
1149
|
+
async authenticateFinesse(username, password, finesseUrl, userRoles, finesseToken) {
|
|
1150
|
+
|
|
802
1151
|
//Authentication of Finesse User, it returns a status code 200 if user found and 401 if unauthorized.
|
|
803
1152
|
let finesseLoginResponse;
|
|
804
1153
|
|
|
805
|
-
if(finesseToken.length == 0){
|
|
806
|
-
finesseLoginResponse = await finesseService.authenticateUserViaFinesse(username,password,finesseUrl);
|
|
1154
|
+
if (finesseToken.length == 0) {
|
|
1155
|
+
finesseLoginResponse = await finesseService.authenticateUserViaFinesse(username, password, finesseUrl);
|
|
807
1156
|
|
|
808
|
-
}else{
|
|
809
|
-
finesseLoginResponse = await finesseService.authenticateUserViaFinesseSSO(username,finesseToken,finesseUrl);
|
|
1157
|
+
} else {
|
|
1158
|
+
finesseLoginResponse = await finesseService.authenticateUserViaFinesseSSO(username, finesseToken, finesseUrl);
|
|
810
1159
|
}
|
|
811
1160
|
|
|
812
1161
|
//If user is SSO then password is not provided, we are setting up a pre-defined password.
|
|
813
|
-
password = (password.length == 0)?"123456":password;
|
|
814
|
-
|
|
1162
|
+
password = (password.length == 0) ? "123456" : password;
|
|
1163
|
+
|
|
815
1164
|
let authenticatedByKeycloak = false;
|
|
816
1165
|
let keycloakAuthToken = null;
|
|
817
1166
|
let timeoutErr = null;
|
|
818
1167
|
|
|
819
|
-
if(finesseLoginResponse.status == 200){
|
|
820
|
-
try{
|
|
821
|
-
|
|
1168
|
+
if (finesseLoginResponse.status == 200) {
|
|
1169
|
+
try {
|
|
1170
|
+
|
|
822
1171
|
//Checking whether finesse user already exist in keycloak and fetch its token
|
|
823
|
-
keycloakAuthToken = await this.getKeycloakTokenWithIntrospect(username,password,keycloakConfig["realm"]);
|
|
1172
|
+
keycloakAuthToken = await this.getKeycloakTokenWithIntrospect(username, password, keycloakConfig["realm"]);
|
|
824
1173
|
authenticatedByKeycloak = true;
|
|
825
|
-
|
|
826
|
-
}catch(err){
|
|
827
1174
|
|
|
828
|
-
|
|
1175
|
+
} catch (err) {
|
|
1176
|
+
|
|
1177
|
+
if (err.response.status == 401) {
|
|
829
1178
|
|
|
830
1179
|
console.log("User doesn't exist in Keycloak, syncing finesse user in keycloak...");
|
|
831
1180
|
|
|
832
|
-
}else{
|
|
1181
|
+
} else {
|
|
833
1182
|
|
|
834
|
-
throw({
|
|
1183
|
+
throw ({
|
|
835
1184
|
"status": err.response.status,
|
|
836
1185
|
"message": err.response.data.error_description
|
|
837
1186
|
});
|
|
838
1187
|
|
|
839
1188
|
}
|
|
840
1189
|
|
|
841
|
-
}finally{
|
|
842
|
-
|
|
843
|
-
//Finesse User not found in keycloak, so we are going to create one.
|
|
844
|
-
if(!authenticatedByKeycloak){
|
|
1190
|
+
} finally {
|
|
845
1191
|
|
|
846
|
-
|
|
1192
|
+
//Finesse User not found in keycloak, so we are going to create one.
|
|
1193
|
+
if (!authenticatedByKeycloak) {
|
|
847
1194
|
|
|
848
|
-
|
|
849
|
-
keycloakAuthToken = await this.getKeycloakTokenWithIntrospect(keycloakConfig["USERNAME_ADMIN"],keycloakConfig["PASSWORD_ADMIN"],keycloakConfig["realm"]);
|
|
850
|
-
}catch(err){
|
|
1195
|
+
try {
|
|
851
1196
|
|
|
852
|
-
|
|
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) {
|
|
853
1200
|
|
|
854
|
-
|
|
855
|
-
'Keycloak login status': 408,
|
|
856
|
-
'keycloak login message': `Keycloak server unaccessable against URL: ${keycloakConfig["auth-server-url"]}`
|
|
857
|
-
});
|
|
1201
|
+
if (err.code == "ETIMEDOUT") {
|
|
858
1202
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
});
|
|
864
|
-
}
|
|
1203
|
+
throw ({
|
|
1204
|
+
'Keycloak login status': 408,
|
|
1205
|
+
'keycloak login message': `Keycloak server unaccessable against URL: ${keycloakConfig["auth-server-url"]}`
|
|
1206
|
+
});
|
|
865
1207
|
|
|
1208
|
+
} else {
|
|
1209
|
+
throw ({
|
|
1210
|
+
"status": err.response.status,
|
|
1211
|
+
"message": "Error While getting Keycloak admin token: " + err.response.data.error_description
|
|
1212
|
+
});
|
|
866
1213
|
}
|
|
867
1214
|
|
|
868
|
-
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
if (keycloakAuthToken.token) {
|
|
869
1218
|
|
|
870
|
-
|
|
1219
|
+
let token = keycloakAuthToken.token;
|
|
871
1220
|
|
|
872
|
-
|
|
873
|
-
|
|
1221
|
+
//validating customer Before Creation
|
|
1222
|
+
let { error, value } = validateUser({ username, password, token, userRoles });
|
|
874
1223
|
|
|
875
|
-
|
|
1224
|
+
if (error) {
|
|
876
1225
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
}
|
|
1226
|
+
throw ({
|
|
1227
|
+
"status": 400,
|
|
1228
|
+
"message": "Error while creation of user, error message: " + error.details[0].message
|
|
1229
|
+
})
|
|
882
1230
|
}
|
|
1231
|
+
}
|
|
883
1232
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
if(userCreated.status == 201){
|
|
1233
|
+
try {
|
|
1234
|
+
//Creating Finesse User inside keycloak.
|
|
1235
|
+
let userCreated = await this.createUser(username, password, keycloakAuthToken.token, userRoles);
|
|
889
1236
|
|
|
890
|
-
|
|
891
|
-
keycloakAuthToken = await this.getKeycloakTokenWithIntrospect(username,password,keycloakConfig["realm"]);
|
|
892
|
-
}
|
|
893
|
-
|
|
1237
|
+
if (userCreated.status == 201) {
|
|
894
1238
|
|
|
895
|
-
|
|
1239
|
+
//Returning the token of recently created User
|
|
1240
|
+
keycloakAuthToken = await this.getKeycloakTokenWithIntrospect(username, password, keycloakConfig["realm"]);
|
|
1241
|
+
}
|
|
896
1242
|
|
|
897
|
-
if(err.code == "ETIMEDOUT"){
|
|
898
1243
|
|
|
899
|
-
|
|
900
|
-
'Keycloak login status': 408,
|
|
901
|
-
'keycloak login message': `Keycloak server unaccessable against URL: ${keycloakConfig["auth-server-url"]}`
|
|
902
|
-
});
|
|
1244
|
+
} catch (err) {
|
|
903
1245
|
|
|
904
|
-
|
|
1246
|
+
if (err.code == "ETIMEDOUT") {
|
|
905
1247
|
|
|
906
|
-
|
|
1248
|
+
throw ({
|
|
1249
|
+
'Keycloak login status': 408,
|
|
1250
|
+
'keycloak login message': `Keycloak server unaccessable against URL: ${keycloakConfig["auth-server-url"]}`
|
|
1251
|
+
});
|
|
907
1252
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
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
|
+
}
|
|
913
1262
|
}
|
|
914
1263
|
}
|
|
915
1264
|
}
|
|
916
|
-
|
|
1265
|
+
|
|
917
1266
|
return keycloakAuthToken;
|
|
918
1267
|
|
|
919
|
-
}else{
|
|
1268
|
+
} else {
|
|
920
1269
|
|
|
921
1270
|
return finesseLoginResponse
|
|
922
1271
|
|
|
923
1272
|
}
|
|
924
1273
|
}
|
|
925
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
|
+
|
|
926
1293
|
}
|
|
927
1294
|
|
|
928
1295
|
function validateUser(userData) {
|
|
@@ -932,7 +1299,7 @@ function validateUser(userData) {
|
|
|
932
1299
|
token: Joi.string().required(),
|
|
933
1300
|
userRoles: Joi.array().items(Joi.string()).allow(null)
|
|
934
1301
|
});
|
|
935
|
-
|
|
1302
|
+
|
|
936
1303
|
return schema.validate(userData);
|
|
937
1304
|
}
|
|
938
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;
|