ef-keycloak-connect 1.9.2 → 1.9.3-RC2
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/.vscode/settings.json +2 -0
- package/README.md +28 -8
- package/package.json +1 -2
- package/services/ciscoSyncService.js +18 -14
- package/services/dynamics365Service.js +5 -5
- package/services/errorService.js +12 -2
- package/services/finesseService.js +6 -0
- package/services/keycloakService.js +1436 -613
- package/services/salesforceService.js +3 -3
- package/services/teamsService.js +3 -3
- package/services/keycloak/README.md +0 -13
- package/services/keycloak/monitoring.js +0 -122
- package/services/keycloak/twofa.js +0 -125
- package/services/keycloak/utils.js +0 -30
package/README.md
CHANGED
|
@@ -40,8 +40,8 @@ This adapter is extended from keycloak-connect and have functionalities of both
|
|
|
40
40
|
- getRealmRoles
|
|
41
41
|
- assignRoleToUser
|
|
42
42
|
- authenticateFinesse
|
|
43
|
-
- createRealmAsTenant
|
|
44
43
|
- externalServiceSso
|
|
44
|
+
- createRealmAsTenant
|
|
45
45
|
|
|
46
46
|
```
|
|
47
47
|
### Example
|
|
@@ -96,14 +96,14 @@ Sample `config` is given below:
|
|
|
96
96
|
"bearer-only": true,
|
|
97
97
|
"TWILIO_SID": "yourTwilioSID",
|
|
98
98
|
"TWILIO_AUTH_TOKEN": "yourTwilioAuthToken",
|
|
99
|
+
"EXTERNAL_USER_PASSWORD": "external_service_user_password",
|
|
99
100
|
"TWILIO_VERIFY_SID": "yourTwilioVerifySID",
|
|
100
101
|
"ef-server-url": "https://<cx instance fqdn>/unified-admin/",
|
|
101
102
|
"FINESSE_USERNAME_ADMIN": "<cisco admin username>",
|
|
102
103
|
"FINESSE_PASSWORD_ADMIN": "<cisco admin username>",
|
|
103
104
|
"SYNC_AGENT_PASSWORD": "<qm sync agent/supervisor password>",
|
|
104
105
|
"MASTER_USERNAME": "master_admin_username",
|
|
105
|
-
"MASTER_PASSWORD": "master_admin_password"
|
|
106
|
-
"EXTERNAL_USER_PASSWORD": "external_service_user_password"
|
|
106
|
+
"MASTER_PASSWORD": "master_admin_password"
|
|
107
107
|
}
|
|
108
108
|
```
|
|
109
109
|
Here is the definition of each property defined in config file/object.
|
|
@@ -126,6 +126,7 @@ Here is the definition of each property defined in config file/object.
|
|
|
126
126
|
- **SCOPE_NAME:** Keycloak Client App scope to use as default scope during Authorization.
|
|
127
127
|
- **bearer-only:** Will keep its value as **true** (This should be set to true for services. If enabled the adapter will not attempt to authenticate users, but only verify bearer tokens. This is OPTIONAL. The default value is false.)
|
|
128
128
|
- **TWILIO_SID:** Your Twilio Account SID.
|
|
129
|
+
- **EXTERNAL_USER_PASSWORD**: While syncing external service user to keycloak, this password value will be assigned as password to it.
|
|
129
130
|
- **TWILIO_AUTH_TOKEN:** Your Twilio account's Auth Token.
|
|
130
131
|
- **TWILIO_VERIFY_SID:** Your Twilio account's Verify Service's SID.
|
|
131
132
|
- *(Each property having TWILIO in its name is optional and is required only in the case of 2FA via SMS.)*
|
|
@@ -136,7 +137,6 @@ Here is the definition of each property defined in config file/object.
|
|
|
136
137
|
- **SYNC_AGENT_PASSWORD**: Password for Users being synced to Keycloak/CX.
|
|
137
138
|
- **MASTER_USERNAME**: Keycloak master admin username.
|
|
138
139
|
- **MASTER_PASSWORD**: Keycloak master password username.
|
|
139
|
-
- **EXTERNAL_USER_PASSWORD**: While syncing external service user to keycloak, this password value will be assigned as password to it.
|
|
140
140
|
|
|
141
141
|
For using keycloak-connect features:
|
|
142
142
|
```js
|
|
@@ -448,9 +448,6 @@ It takes 5 arguments:
|
|
|
448
448
|
- finesse_server_url: The url of finesse server (In case of normal keycloak auth, send this parameter as **' '**)
|
|
449
449
|
- user_roles: The array containing user_roles, it will be used to assign roles to finesse user while synching it with Keycloak (for normal auth send it as [ ]).
|
|
450
450
|
- finesse_token: acess token for finesse SSO authentication (It will be passed if Finesse SSO instance is connected, in case of non SSO will pass empty string **' '** as argument)
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
451
|
|
|
455
452
|
##### Example of SSO Finesse Auth:
|
|
456
453
|
|
|
@@ -459,7 +456,6 @@ It takes 5 arguments:
|
|
|
459
456
|
|
|
460
457
|
##### Example of non SSO Finesse Auth:
|
|
461
458
|
|
|
462
|
-
authenticateFinesse('johndoe', '12345', `https://${finesse_server_url}:${port}`, ['agent','supervisor'], '')
|
|
463
459
|
|
|
464
460
|
### externalServiceSso( userRolesArr, validationToken, externalServiceURL, type )
|
|
465
461
|
|
|
@@ -471,6 +467,30 @@ It takes 4 arguments:
|
|
|
471
467
|
- externalServiceURL: The url of external service server e.g in case of dynamics365: **'https://{fqdn}/api/data/v9.0'** , in case of salesforce: **'https://{salesforce service fqdn}.com'**
|
|
472
468
|
- type: Type of external service. In case of Dynamics365, the type is: **dynamics365**. In case of Salesforce, the type is: **salesforce**
|
|
473
469
|
|
|
470
|
+
#### Example (Dynamics365)
|
|
471
|
+
|
|
472
|
+
```js
|
|
473
|
+
await keycloak.externalServiceSso(
|
|
474
|
+
['agent', 'supervisor'],
|
|
475
|
+
'eyJ0eXAiOiJKV1QiLCJhbGci...',
|
|
476
|
+
'https://yourcrm.dynamics.com/api/data/v9.0',
|
|
477
|
+
'dynamics365'
|
|
478
|
+
);
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
#### Example (Salesforce)
|
|
482
|
+
|
|
483
|
+
```js
|
|
484
|
+
await keycloak.externalServiceSso(
|
|
485
|
+
['agent'],
|
|
486
|
+
'eyJ0eXAiOiJKV1QiLCJhbGci...',
|
|
487
|
+
'https://yourorg.salesforce.com',
|
|
488
|
+
'salesforce'
|
|
489
|
+
);
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
authenticateFinesse('johndoe', '12345', `https://${finesse_server_url}:${port}`, ['agent','supervisor'], '')
|
|
493
|
+
|
|
474
494
|
### generateAccessTokenFromRefreshToken(refreshToken)
|
|
475
495
|
|
|
476
496
|
This function generates a new access_token by using the refreshToken received in parameter.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ef-keycloak-connect",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.3-RC2",
|
|
4
4
|
"description": "Node JS keycloak adapter for authentication and authorization.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
"keycloak-connect": "^26.0.7",
|
|
21
21
|
"qrcode": "^1.5.4",
|
|
22
22
|
"speakeasy": "^2.0.0",
|
|
23
|
-
"twilio": "^5.2.2",
|
|
24
23
|
"xml2js": "^0.6.2"
|
|
25
24
|
}
|
|
26
25
|
}
|
|
@@ -3,7 +3,7 @@ const https = require( 'https' );
|
|
|
3
3
|
|
|
4
4
|
let requestController = require( "../controller/requestController.js" );
|
|
5
5
|
const ErrorService = require( './errorService.js' );
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
|
|
8
8
|
const errorService = new ErrorService();
|
|
9
9
|
let realmRoles = [];
|
|
@@ -227,8 +227,8 @@ class CiscoSyncService {
|
|
|
227
227
|
credentials: [
|
|
228
228
|
{
|
|
229
229
|
type: "password",
|
|
230
|
-
value: "Expertflow464",
|
|
231
|
-
temporary:
|
|
230
|
+
value: keycloakConfig[ "DEFAULT_USER_PASSWORD" ] || "Expertflow464",
|
|
231
|
+
temporary: true,
|
|
232
232
|
},
|
|
233
233
|
],
|
|
234
234
|
attributes: {
|
|
@@ -263,7 +263,7 @@ class CiscoSyncService {
|
|
|
263
263
|
//Get list of all the roles in keycloak realm
|
|
264
264
|
/* Storing all the realm roles in Global realmRoles list.If some role come from finesse which doesn't
|
|
265
265
|
exist in realmRoles list then we call keycloak roles api again to update realmRoles list. */
|
|
266
|
-
if ( ciscoUser.roles
|
|
266
|
+
if ( Array.isArray( ciscoUser.roles ) && ciscoUser.roles.length > 0 ) {
|
|
267
267
|
|
|
268
268
|
if ( realmRoles.length > 0 ) {
|
|
269
269
|
|
|
@@ -572,8 +572,8 @@ class CiscoSyncService {
|
|
|
572
572
|
let cxTeams = await this.fetchCXTeams( keycloakConfig[ "ef-server-url" ] );
|
|
573
573
|
|
|
574
574
|
// Fetch Keycloak users
|
|
575
|
-
let keycloakUsers = await this.fetchKeycloakUsers( keycloakConfig
|
|
576
|
-
const keycloakUsersByRole = await this.fetchKeycloakUsersByRole( keycloakConfig
|
|
575
|
+
let keycloakUsers = await this.fetchKeycloakUsers( keycloakConfig, adminToken );
|
|
576
|
+
const keycloakUsersByRole = await this.fetchKeycloakUsersByRole( keycloakConfig, adminToken );
|
|
577
577
|
const keycloakUsersByPermissionGroups = await this.fetchKeycloakUsersByPermissionGroups( keycloakConfig, adminToken );
|
|
578
578
|
|
|
579
579
|
if ( ciscoUsers.length > 0 && ciscoTeams.length > 0 ) {
|
|
@@ -585,7 +585,7 @@ class CiscoSyncService {
|
|
|
585
585
|
await this.syncUsersToCX( ciscoUsers, keycloakUsers, keycloakUsersByRole, keycloakUsersByPermissionGroups, keycloakConfig, adminToken );
|
|
586
586
|
|
|
587
587
|
//Once users are synced, we fetch updated keycloak users list.
|
|
588
|
-
keycloakUsers = await this.fetchKeycloakUsers( keycloakConfig
|
|
588
|
+
keycloakUsers = await this.fetchKeycloakUsers( keycloakConfig, adminToken );
|
|
589
589
|
|
|
590
590
|
let userSyncedToMongo = await this.syncKeycloakUsersToCXMongo( keycloakConfig[ "USERNAME_ADMIN" ], keycloakConfig[ "PASSWORD_ADMIN" ], keycloakConfig[ "ef-server-url" ] );
|
|
591
591
|
|
|
@@ -739,6 +739,9 @@ class CiscoSyncService {
|
|
|
739
739
|
}
|
|
740
740
|
} )
|
|
741
741
|
|
|
742
|
+
} else {
|
|
743
|
+
|
|
744
|
+
reject( er );
|
|
742
745
|
}
|
|
743
746
|
|
|
744
747
|
}
|
|
@@ -912,6 +915,9 @@ class CiscoSyncService {
|
|
|
912
915
|
}
|
|
913
916
|
} )
|
|
914
917
|
|
|
918
|
+
} else {
|
|
919
|
+
|
|
920
|
+
reject( er );
|
|
915
921
|
}
|
|
916
922
|
|
|
917
923
|
}
|
|
@@ -919,11 +925,11 @@ class CiscoSyncService {
|
|
|
919
925
|
} );
|
|
920
926
|
}
|
|
921
927
|
|
|
922
|
-
async fetchKeycloakUsers(
|
|
928
|
+
async fetchKeycloakUsers( keycloakConfig, adminToken ) {
|
|
923
929
|
|
|
924
930
|
return new Promise( async ( resolve, reject ) => {
|
|
925
931
|
|
|
926
|
-
let URL =
|
|
932
|
+
let URL = keycloakConfig[ "auth-server-url" ] + `admin/realms/${keycloakConfig[ "realm" ]}/users?max=100000`;
|
|
927
933
|
|
|
928
934
|
let config = {
|
|
929
935
|
|
|
@@ -933,8 +939,6 @@ class CiscoSyncService {
|
|
|
933
939
|
Accept: "application/json",
|
|
934
940
|
"cache-control": "no-cache",
|
|
935
941
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
936
|
-
},
|
|
937
|
-
headers: {
|
|
938
942
|
Authorization: "Bearer " + adminToken,
|
|
939
943
|
},
|
|
940
944
|
|
|
@@ -952,7 +956,7 @@ class CiscoSyncService {
|
|
|
952
956
|
} )
|
|
953
957
|
}
|
|
954
958
|
|
|
955
|
-
async fetchKeycloakUsersByRole(
|
|
959
|
+
async fetchKeycloakUsersByRole( keycloakConfig, adminToken ) {
|
|
956
960
|
|
|
957
961
|
return new Promise( async ( resolve, reject ) => {
|
|
958
962
|
|
|
@@ -963,7 +967,7 @@ class CiscoSyncService {
|
|
|
963
967
|
// Use Promise.all to handle multiple role requests concurrently
|
|
964
968
|
for ( let role of roles ) {
|
|
965
969
|
|
|
966
|
-
const URL = `${
|
|
970
|
+
const URL = `${keycloakConfig[ "auth-server-url" ]}admin/realms/${keycloakConfig[ "realm" ]}/roles/${role}/users?max=1000000`;
|
|
967
971
|
|
|
968
972
|
const config = {
|
|
969
973
|
method: "get",
|
|
@@ -1023,7 +1027,7 @@ class CiscoSyncService {
|
|
|
1023
1027
|
// Use Promise.all to handle multiple role requests concurrently
|
|
1024
1028
|
for ( let group of permissionGroupsObj ) {
|
|
1025
1029
|
|
|
1026
|
-
const URL = `${keycloakConfig[ "auth-server-url" ]}admin/realms/
|
|
1030
|
+
const URL = `${keycloakConfig[ "auth-server-url" ]}admin/realms/${keycloakConfig[ "realm" ]}/groups/${group.id}/members?max=1000000`;
|
|
1027
1031
|
|
|
1028
1032
|
const config = {
|
|
1029
1033
|
|
|
@@ -15,12 +15,12 @@ class Dynamics365Service {
|
|
|
15
15
|
|
|
16
16
|
let config = {
|
|
17
17
|
method: 'get',
|
|
18
|
-
'Content-Type': 'application/json',
|
|
19
|
-
'Accept': 'application/json',
|
|
20
|
-
'OData-MaxVersion': '4.0',
|
|
21
|
-
'OData-Version': '4.0',
|
|
22
18
|
url: URL,
|
|
23
19
|
headers: {
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
'Accept': 'application/json',
|
|
22
|
+
'OData-MaxVersion': '4.0',
|
|
23
|
+
'OData-Version': '4.0',
|
|
24
24
|
'Authorization': `Bearer ${validationToken}`
|
|
25
25
|
},
|
|
26
26
|
//disable ssl
|
|
@@ -106,4 +106,4 @@ class Dynamics365Service {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
module.exports = Dynamics365Service;
|
|
109
|
+
module.exports = Dynamics365Service;
|
package/services/errorService.js
CHANGED
|
@@ -82,10 +82,20 @@ class ErrorService {
|
|
|
82
82
|
};
|
|
83
83
|
case 'invalid_grant':
|
|
84
84
|
|
|
85
|
+
let reasonToReturn
|
|
86
|
+
if ( err.response.status === 400 ) {
|
|
87
|
+
if(errorData.error_description.includes('Account disabled')){
|
|
88
|
+
reasonToReturn = "Account Disabled: User account has been disabled in Keycloak."
|
|
89
|
+
}
|
|
90
|
+
else
|
|
91
|
+
reasonToReturn = 'Password Update Required: Please update your password, as it was set temporarily by the admin or has expired.'
|
|
92
|
+
}
|
|
93
|
+
else reasonToReturn = 'Invalid User Credentials: The provided credentials are not valid. Please enter valid user credentials.'
|
|
85
94
|
return {
|
|
86
95
|
status: err.response.status,
|
|
87
|
-
reason:
|
|
96
|
+
reason: reasonToReturn,
|
|
88
97
|
};
|
|
98
|
+
|
|
89
99
|
default:
|
|
90
100
|
|
|
91
101
|
if ( typeof ( err.response.data ) === "object" ) {
|
|
@@ -115,7 +125,7 @@ class ErrorService {
|
|
|
115
125
|
} else {
|
|
116
126
|
|
|
117
127
|
if ( typeof ( err.response.data ) === "object" ) {
|
|
118
|
-
|
|
128
|
+
|
|
119
129
|
return {
|
|
120
130
|
status: err.response.status,
|
|
121
131
|
reason: err.response.data.error
|