ef-keycloak-connect 1.8.4-patch-2.0 → 1.8.6

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/README.md CHANGED
@@ -41,6 +41,7 @@ This adapter is extended from keycloak-connect and have functionalities of both
41
41
  - assignRoleToUser
42
42
  - authenticateFinesse
43
43
  - createRealmAsTenant
44
+ - dynamics365Sso
44
45
 
45
46
  ```
46
47
  ### Example
@@ -445,6 +446,9 @@ It takes 5 arguments:
445
446
  - finesse_server_url: The url of finesse server (In case of normal keycloak auth, send this parameter as **' '**)
446
447
  - 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 [ ]).
447
448
  - 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)
449
+
450
+
451
+
448
452
 
449
453
  ##### Example of SSO Finesse Auth:
450
454
 
@@ -455,6 +459,15 @@ It takes 5 arguments:
455
459
 
456
460
  authenticateFinesse('johndoe', '12345', `https://${finesse_server_url}:${port}`, ['agent','supervisor'], '')
457
461
 
462
+ ### dynamics365Sso( userRoles, validationToken, dynamics365Url )
463
+
464
+ This function sync microsoft dynamics 365 user in keycloak, it first authenticates user from dynamics365, then check for its existance in keycloak. If it exists in keycloak then generates an access_token along with role mapping and team mapping and return it to user. If user doesn't exist then it creates a user, assign it roles and team and return the access_token along with role mapping/team mapping for newly created user.
465
+
466
+ It takes 3 arguments:
467
+ - userRoles: The array containing user roles, it will be used to assign roles to dynamics365 user while synching it with Keycloak e.g **['agent']**.
468
+ - validationToken: acess token for dynamics365 user validation and to get user details.
469
+ - dynamics365Url: The url of dynamics365 server e.g **'https://{fqdn}/api/data/v9.0'**
470
+
458
471
  ### generateAccessTokenFromRefreshToken(refreshToken)
459
472
 
460
473
  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.8.4-patch-2.0",
3
+ "version": "1.8.6",
4
4
  "description": "Node JS keycloak adapter for authentication and authorization.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,109 @@
1
+ const parseXMLString = require( 'xml2js' ).parseString;
2
+ const https = require( 'https' );
3
+
4
+ let requestController = require( "../controller/requestController.js" );
5
+
6
+ class Dynamics365Service {
7
+
8
+
9
+ async authenticateUserViaDynamics365( validationToken, dynamics365Url ) {
10
+
11
+ return new Promise( async ( resolve, reject ) => {
12
+
13
+ let URL = dynamics365Url + '/WhoAmI'
14
+
15
+
16
+ let config = {
17
+ method: 'get',
18
+ 'Content-Type': 'application/json',
19
+ 'Accept': 'application/json',
20
+ 'OData-MaxVersion': '4.0',
21
+ 'OData-Version': '4.0',
22
+ url: URL,
23
+ headers: {
24
+ 'Authorization': `Bearer ${validationToken}`
25
+ },
26
+ //disable ssl
27
+ httpsAgent: new https.Agent( { rejectUnauthorized: false } )
28
+ };
29
+
30
+ try {
31
+
32
+ let whoAmIResponse = await requestController.httpRequest( config, false );
33
+
34
+ let userId = whoAmIResponse?.data?.UserId;
35
+
36
+ try {
37
+
38
+ let URL1 = `${dynamics365Url}/systemusers(${userId})?$select=fullname,firstname,middlename,lastname,internalemailaddress,title,isdisabled,businessunitid,mobilephone,createdon,modifiedon`
39
+ config.url = URL1;
40
+ config.maxBodyLength = 'Infinity';
41
+
42
+ let userObjectResponse = await requestController.httpRequest( config, true );
43
+ let userObject = userObjectResponse?.data;
44
+
45
+
46
+ resolve( {
47
+ 'data': userObject,
48
+ 'status': userObjectResponse?.status
49
+ } );
50
+
51
+ }
52
+ catch ( er ) {
53
+
54
+ if ( er.code == "ENOTFOUND" ) {
55
+
56
+ reject( {
57
+ error_message: "Dynamics365 Authentication Error: An error occurred while getting the user data from Dynamics365.",
58
+ error_detail: {
59
+ status: 408,
60
+ reason: `Dynamics365 server not accessible against URL: ${dynamics365Url}`
61
+ }
62
+ } )
63
+
64
+ } else if ( er.response ) {
65
+
66
+ reject( {
67
+ error_message: "Dynamics365 Authentication Error: An error occurred while getting the user data from Dynamics365.",
68
+ error_detail: {
69
+ status: er.response.status,
70
+ reason: er.response.statusText
71
+ }
72
+ } )
73
+
74
+ }
75
+
76
+ }
77
+
78
+ }
79
+ catch ( er ) {
80
+
81
+ if ( er.code == "ENOTFOUND" ) {
82
+
83
+ reject( {
84
+ error_message: "Dynamics365 Authentication Error: An error occurred while validating user in Dynamics365.",
85
+ error_detail: {
86
+ status: 408,
87
+ reason: `Finesse server not accessible against URL: ${dynamics365Url}`
88
+ }
89
+ } )
90
+
91
+ } else if ( er.response ) {
92
+
93
+ reject( {
94
+ error_message: "Dynamics365 Authentication Error: An error occurred while validating user in Dynamics365.",
95
+ error_detail: {
96
+ status: er.response.status,
97
+ reason: er.response.statusText
98
+ }
99
+ } )
100
+
101
+ }
102
+
103
+ }
104
+
105
+ } );
106
+ }
107
+ }
108
+
109
+ module.exports = Dynamics365Service;
@@ -17,6 +17,7 @@ const FinesseService = require( "./finesseService" );
17
17
  const TeamsService = require( "./teamsService" );
18
18
  const ErrorService = require( './errorService.js' );
19
19
  const CiscoSyncService = require( './ciscoSyncService.js' );
20
+ const Dynamics365Service = require( './dynamics365Service.js' );
20
21
 
21
22
  const twilio = require( 'twilio' )
22
23
  let twilioClient = null // will be initialized in constructor using config file
@@ -25,6 +26,7 @@ const finesseService = new FinesseService();
25
26
  const teamsService = new TeamsService();
26
27
  const errorService = new ErrorService();
27
28
  const ciscoSyncService = new CiscoSyncService();
29
+ const dynamics365Service = new Dynamics365Service();
28
30
 
29
31
  class KeycloakService extends Keycloak {
30
32
 
@@ -2704,24 +2706,27 @@ class KeycloakService extends Keycloak {
2704
2706
 
2705
2707
  let data = {
2706
2708
 
2707
- username: userObject.username,
2708
- firstName: userObject.firstName,
2709
- lastName: userObject.lastName,
2709
+ username: ( userObject?.type == "dynamics365" ) ? userObject?.fullname : userObject?.username,
2710
+ firstName: ( userObject?.type == "dynamics365" ) ? userObject?.firstname : userObject?.firstName,
2711
+ lastName: ( userObject?.type == "dynamics365" ) ? userObject?.lastname : userObject?.lastName,
2712
+ email: ( userObject?.type == "dynamics365" ) ? userObject?.internalemailaddress : '',
2710
2713
  enabled: true,
2711
2714
  credentials: [
2712
2715
  {
2713
2716
  type: "password",
2714
- value: userObject.password,
2717
+ value: userObject?.password,
2715
2718
  temporary: false,
2716
2719
  },
2717
2720
  ],
2718
2721
  attributes: {
2719
- "user_name": `${userObject.loginName}`,
2720
- "extension": `${userObject.extension}`
2722
+ "user_name": `${( userObject?.loginName ) ? userObject?.loginName : ''}`,
2723
+ "extension": `${( userObject?.extension ) ? userObject?.extension : 'CISCO'}`
2721
2724
  },
2722
2725
  groups: assignGroups
2723
2726
  };
2724
2727
 
2728
+ userObject?.type == "dynamics365" && ( data.attributes.userid = userObject?.ownerid );
2729
+
2725
2730
  let config = {
2726
2731
 
2727
2732
  method: "post",
@@ -2795,7 +2800,96 @@ class KeycloakService extends Keycloak {
2795
2800
  }
2796
2801
 
2797
2802
 
2798
- let ciscoTeamId = userObject.group.id;
2803
+ if ( userObject?.type == "dynamics365" ) {
2804
+
2805
+ let tempUrl1 = `${this.keycloakConfig[ "ef-server-url" ]}team`;
2806
+
2807
+ let tempConfig1 = {
2808
+
2809
+ url: tempUrl1,
2810
+ method: "get",
2811
+ headers: {
2812
+ Accept: "application/json",
2813
+ "cache-control": "no-cache",
2814
+ "Content-Type": "application/x-www-form-urlencoded",
2815
+ }
2816
+
2817
+ };
2818
+
2819
+ try {
2820
+
2821
+ let getTeamsList = await requestController.httpRequest( tempConfig1, false );
2822
+
2823
+ // Access the actual array of teams from the .data property.
2824
+ const teamsData = getTeamsList?.data;
2825
+
2826
+ // Check if the teamsData array is empty or not an array.
2827
+ if ( !Array.isArray( teamsData ) || teamsData.length === 0 ) {
2828
+
2829
+ console.log( "teamsData array is empty or invalid. Assigning 1 to userObject.group.id." );
2830
+ userObject.group.id = 1;
2831
+
2832
+ } else {
2833
+
2834
+ const findDefaultTeamId = ( teamsArray ) => {
2835
+
2836
+ // Rule 1: Find a team with team_name "default" (case-insensitive).
2837
+ const defaultTeam = teamsArray.find(
2838
+ ( team ) => team.team_name && team.team_name.toLowerCase() === "default"
2839
+ );
2840
+
2841
+ if ( defaultTeam ) {
2842
+ console.log( 'Rule 1 matched: Found team with name "default".' );
2843
+ return defaultTeam.team_Id;
2844
+ }
2845
+
2846
+ console.log( 'Rule 1 failed: No team with name "default" found.' );
2847
+
2848
+ // Rule 2: If no "default" team, find a team with team_Id of 1 whose name is NOT "default".
2849
+ const teamWithIdOne = teamsArray.find(
2850
+ ( team ) =>
2851
+ ( team.team_Id === 1 || team.team_Id === "1" ) &&
2852
+ team.team_name?.toLowerCase() !== "default"
2853
+ );
2854
+
2855
+ if ( teamWithIdOne ) {
2856
+ console.log( "Rule 2 matched: Found a team with team_Id of 1 and a non-default name." );
2857
+ return crypto.randomUUID();
2858
+ }
2859
+
2860
+ console.log( "Rule 2 failed: No suitable team with team_Id of 1 found." );
2861
+
2862
+ // Rule 3: If no default team was found and no suitable team with ID 1 was found, return a random ID.
2863
+ console.log( "Rule 3 matched: Defaulting to a new random UUID." );
2864
+ return 1;
2865
+ };
2866
+
2867
+ // Call the function with the fetched list of teams
2868
+ const defaultTeamId = findDefaultTeamId( teamsData );
2869
+
2870
+ // Assign the resulting ID to the userObject
2871
+ userObject.groupId = defaultTeamId;
2872
+
2873
+ // You can now use the 'defaultTeamId' variable and the updated userObject
2874
+ console.log( `The determined default Team ID is: ${defaultTeamId}` );
2875
+ }
2876
+
2877
+ } catch ( er ) {
2878
+
2879
+ let error = await errorService.handleError( er );
2880
+
2881
+ reject( {
2882
+
2883
+ error_message: "Dynamics365 Team Sync Error: Error occured while checking for default team in CX.",
2884
+ error_detail: error
2885
+ } );
2886
+ }
2887
+
2888
+ }
2889
+
2890
+ ( userObject?.type == "dynamics365" ) && ( userObject.groupName = "default" );
2891
+
2892
+ let ciscoTeamId = ( userObject?.type == "dynamics365" ) ? userObject.groupId : userObject.group.id;
2799
2893
 
2800
2894
  //Check whether team of Agent already exists in CX Core or not
2801
2895
  let URL1 = `${this.keycloakConfig[ "ef-server-url" ]}team?ids=${ciscoTeamId}`;
@@ -2826,7 +2920,7 @@ class KeycloakService extends Keycloak {
2826
2920
 
2827
2921
  try {
2828
2922
 
2829
- let getAgentCXTeam = await requestController.httpRequest( config1, false );
2923
+ let getAgentCXTeam = await requestController.httpRequest( config1, true );
2830
2924
 
2831
2925
  let createAgentCXTeam;
2832
2926
 
@@ -2837,8 +2931,8 @@ class KeycloakService extends Keycloak {
2837
2931
  let URL2 = `${this.keycloakConfig[ "ef-server-url" ]}team`;
2838
2932
 
2839
2933
  let data = {
2840
- "team_Id": userObject.group.id,
2841
- "team_name": userObject.group.name,
2934
+ "team_Id": ( userObject?.type == "dynamics365" ) ? ciscoTeamId : userObject.group.id,
2935
+ "team_name": ( userObject?.type == "dynamics365" ) ? userObject?.groupName : userObject.group.name,
2842
2936
  "supervisor_Id": "",
2843
2937
  "source": "CISCO",
2844
2938
  "created_by": "1"
@@ -2847,10 +2941,13 @@ class KeycloakService extends Keycloak {
2847
2941
  config2.url = URL2;
2848
2942
  config2.data = data;
2849
2943
 
2944
+ console.log( 'create team if does not exist ' )
2945
+
2850
2946
  try {
2851
2947
 
2852
2948
  //Creating CX team of Agent
2853
- createAgentCXTeam = await requestController.httpRequest( config2, false );
2949
+ createAgentCXTeam = await requestController.httpRequest( config2, true );
2950
+ console.log( createAgentCXTeam );
2854
2951
 
2855
2952
  } catch ( er ) {
2856
2953
 
@@ -2870,18 +2967,57 @@ class KeycloakService extends Keycloak {
2870
2967
 
2871
2968
  let data = {
2872
2969
  "id": userId,
2873
- "username": userObject.username.toLocaleLowerCase(),
2874
- "firstName": userObject.firstName,
2875
- "lastName": userObject.lastName,
2970
+ "username": ( userObject?.type == "dynamics365" ) ? userObject?.fullname : userObject?.username.toLocaleLowerCase(),
2971
+ "firstName": ( userObject?.type == "dynamics365" ) ? userObject.firstname : userObject.firstName,
2972
+ "lastName": ( userObject?.type == "dynamics365" ) ? userObject.lastname : userObject.lastName,
2876
2973
  "roles": userObject.roles
2877
2974
  }
2878
2975
 
2879
2976
  config2.url = URL3;
2880
2977
  config2.data = data;
2881
2978
 
2979
+ console.log( 'send data to db' );
2980
+
2882
2981
  try {
2883
2982
 
2884
- let sendSupUserToCX = await requestController.httpRequest( config2, false );
2983
+ let sendSupUserToCX = requestController.httpRequest( config2, false ).then( async res => {
2984
+
2985
+ //Assign Agent to a team
2986
+ let URL4 = `${this.keycloakConfig[ "ef-server-url" ]}team/${ciscoTeamId}/member`;
2987
+
2988
+ data = {
2989
+ "type": "agent",
2990
+ "usernames": [ ( userObject?.type == "dynamics365" ) ? userObject?.fullname : userObject?.username.toLocaleLowerCase() ]
2991
+ }
2992
+
2993
+ config2.url = URL4;
2994
+ config2.data = data;
2995
+
2996
+ console.log( 'adding user as member of team' );
2997
+
2998
+ try {
2999
+
3000
+ //Assigning Agent to CX team
3001
+ let assignAgentToTeam = await requestController.httpRequest( config2, false );
3002
+ return assignAgentToTeam;
3003
+
3004
+ } catch ( er ) {
3005
+
3006
+ let error = await errorService.handleError( er );
3007
+
3008
+ return reject( {
3009
+
3010
+ error_message: "Finesse Team Sync Error: Error occured while assigning agent to cx core team.",
3011
+ error_detail: error
3012
+ } );
3013
+ }
3014
+
3015
+
3016
+
3017
+ } ).catch( err => {
3018
+ console.log( err );
3019
+
3020
+ } );
2885
3021
 
2886
3022
  } catch ( er ) {
2887
3023
 
@@ -2894,21 +3030,24 @@ class KeycloakService extends Keycloak {
2894
3030
  } );
2895
3031
  }
2896
3032
 
2897
- //Assign Agent to a team
2898
- let URL4 = `${this.keycloakConfig[ "ef-server-url" ]}team/${userObject.group.id}/member`;
3033
+ /* //Assign Agent to a team
3034
+ let URL4 = `${this.keycloakConfig[ "ef-server-url" ]}team/${ciscoTeamId}/member`;
2899
3035
 
2900
3036
  data = {
2901
3037
  "type": "agent",
2902
- "usernames": [ userObject.username.toLocaleLowerCase() ]
3038
+ "usernames": [ ( userObject?.type == "dynamics365" ) ? userObject?.fullname : userObject?.username.toLocaleLowerCase() ]
2903
3039
  }
2904
3040
 
2905
3041
  config2.url = URL4;
2906
3042
  config2.data = data;
2907
3043
 
3044
+ console.log( 'adding user as member of team' );
3045
+ console.log( config2 );
3046
+
2908
3047
  try {
2909
3048
 
2910
3049
  //Assigning Agent to CX team
2911
- let assignAgentToTeam = await requestController.httpRequest( config2, false );
3050
+ let assignAgentToTeam = await requestController.httpRequest( config2, true );
2912
3051
 
2913
3052
  } catch ( er ) {
2914
3053
 
@@ -2919,7 +3058,7 @@ class KeycloakService extends Keycloak {
2919
3058
  error_message: "Finesse Team Sync Error: Error occured while assigning agent to cx core team.",
2920
3059
  error_detail: error
2921
3060
  } );
2922
- }
3061
+ }*/
2923
3062
 
2924
3063
  } catch ( er ) {
2925
3064
 
@@ -2934,7 +3073,7 @@ class KeycloakService extends Keycloak {
2934
3073
 
2935
3074
 
2936
3075
 
2937
- if ( userObject.roles.includes( "supervisor" ) && userObject.supervisedGroups.length > 0 ) {
3076
+ if ( userObject.roles.includes( "supervisor" ) && userObject.supervisedGroups.length > 0 && userObject?.type !== "dynamics365" ) {
2938
3077
 
2939
3078
  for ( let supervisedGroup of userObject.supervisedGroups ) {
2940
3079
 
@@ -3120,12 +3259,14 @@ class KeycloakService extends Keycloak {
3120
3259
  let rptToken = await this.getTokenRPT( username, password, keycloakAuthToken.access_token );
3121
3260
  let introspectToken = await this.getIntrospectToken( rptToken.access_token );
3122
3261
 
3262
+
3123
3263
  let keyObj = {
3124
3264
  id: introspectToken.sub,
3125
3265
  username: introspectToken.username,
3126
3266
  firstName: introspectToken.given_name,
3127
3267
  lastName: introspectToken.family_name,
3128
3268
  roles: introspectToken.realm_access.roles,
3269
+ email: introspectToken.email,
3129
3270
  permittedResources: {
3130
3271
  Resources: introspectToken.authorization.permissions,
3131
3272
  }
@@ -3147,7 +3288,7 @@ class KeycloakService extends Keycloak {
3147
3288
  try {
3148
3289
 
3149
3290
  let userDataResponse = await requestController.httpRequest( config, false );
3150
- userAttributes = userDataResponse.data.attributes;
3291
+ userAttributes = userDataResponse?.data?.attributes;
3151
3292
 
3152
3293
  } catch ( err ) {
3153
3294
 
@@ -3161,24 +3302,34 @@ class KeycloakService extends Keycloak {
3161
3302
 
3162
3303
  }
3163
3304
 
3305
+ finObj.username = ( finObj?.type == "dynamics365" ) ? finObj?.fullname : finObj?.username;
3306
+ finObj.firstName = ( finObj?.type == "dynamics365" ) ? finObj?.firstname : finObj?.firstName;
3307
+ finObj.lastName = ( finObj?.type == "dynamics365" ) ? finObj?.lastname : finObj?.lastName;
3308
+ finObj.email = ( finObj?.type == "dynamics365" ) ? finObj?.internalemailaddress : '';
3309
+
3164
3310
  //Comparing the basic info of Finesse User and Normal User.
3165
3311
  if ( ( finObj.username ).toLowerCase() != keyObj.username
3166
3312
  || finObj.firstName != keyObj.firstName
3167
3313
  || finObj.lastName != keyObj.lastName
3168
- || ( userAttributes.user_name && finObj.loginName !== userAttributes.user_name[ 0 ] )
3169
- || ( userAttributes.extension && finObj.extension !== userAttributes.extension[ 0 ] )
3170
- || ( !userAttributes.user_name )
3314
+ || finObj.email !== keyObj.email
3315
+ || ( userAttributes.user_name && finObj?.loginName !== userAttributes?.user_name[ 0 ] )
3316
+ || ( userAttributes.extension && finObj?.extension !== userAttributes?.extension[ 0 ] )
3317
+ || ( userAttributes.userid && finObj?.ownerid !== userAttributes?.userid )
3318
+ || ( finObj?.type !== "dynamics365" && !userAttributes.user_name )
3171
3319
  ) {
3172
3320
 
3173
3321
  data = {
3174
- username: ( finObj.username ).toLowerCase(),
3175
- firstName: finObj.firstName,
3176
- lastName: finObj.lastName,
3322
+ username: ( finObj?.username ).toLowerCase(),
3323
+ firstName: finObj?.firstName,
3324
+ lastName: finObj?.lastName,
3325
+ email: finObj?.email,
3177
3326
  attributes: {
3178
- "user_name": `${finObj.loginName}`,
3179
- "extension": `${finObj.extension}`
3327
+ "user_name": `${( finObj?.loginName ) ? finObj?.loginName : ''}`,
3328
+ "extension": `${( finObj?.extension ) ? finObj?.extension : 'CISCO'}`
3180
3329
  }
3181
3330
  };
3331
+
3332
+ finObj?.type == "dynamics365" && ( data.attributes.userid = finObj?.ownerid );
3182
3333
  }
3183
3334
 
3184
3335
  if ( Object.keys( data ).length > 0 ) {
@@ -3314,11 +3465,103 @@ class KeycloakService extends Keycloak {
3314
3465
 
3315
3466
  let userTeams = await requestController.httpRequest( config1, true );
3316
3467
 
3468
+ if ( finObj?.type == "dynamics365" ) {
3469
+
3470
+ let tempUrl1 = `${this.keycloakConfig[ "ef-server-url" ]}team`;
3471
+
3472
+ let tempConfig1 = {
3473
+
3474
+ url: tempUrl1,
3475
+ method: "get",
3476
+ headers: {
3477
+ Accept: "application/json",
3478
+ "cache-control": "no-cache",
3479
+ "Content-Type": "application/x-www-form-urlencoded",
3480
+ }
3481
+
3482
+ };
3483
+
3484
+ try {
3485
+
3486
+ let getTeamsList = await requestController.httpRequest( tempConfig1, false );
3487
+
3488
+ // Access the actual array of teams from the .data property.
3489
+ const teamsData = getTeamsList?.data;
3490
+
3491
+ // Check if the teamsData array is empty or not an array.
3492
+ if ( !Array.isArray( teamsData ) || teamsData.length === 0 ) {
3493
+
3494
+ console.log( "teamsData array is empty or invalid. Assigning 1 to finObject.group.id." );
3495
+ finObj.group.id = 1;
3496
+
3497
+ } else {
3498
+
3499
+ const findDefaultTeamId = ( teamsArray ) => {
3500
+
3501
+ // Rule 1: Find a team with team_name "default" (case-insensitive).
3502
+ const defaultTeam = teamsArray.find(
3503
+ ( team ) => team.team_name && team.team_name.toLowerCase() === "default"
3504
+ );
3505
+
3506
+ if ( defaultTeam ) {
3507
+ console.log( 'Rule 1 matched: Found team with name "default".' );
3508
+ return defaultTeam.team_Id;
3509
+ }
3510
+
3511
+ console.log( 'Rule 1 failed: No team with name "default" found.' );
3512
+
3513
+ // Rule 2: If no "default" team, find a team with team_Id of 1 whose name is NOT "default".
3514
+ const teamWithIdOne = teamsArray.find(
3515
+ ( team ) =>
3516
+ ( team.team_Id === 1 || team.team_Id === "1" ) &&
3517
+ team.team_name?.toLowerCase() !== "default"
3518
+ );
3519
+
3520
+ if ( teamWithIdOne ) {
3521
+ console.log( "Rule 2 matched: Found a team with team_Id of 1 and a non-default name." );
3522
+ return crypto.randomUUID();
3523
+ }
3524
+
3525
+ console.log( "Rule 2 failed: No suitable team with team_Id of 1 found." );
3526
+
3527
+ // Rule 3: If no default team was found and no suitable team with ID 1 was found, return a random ID.
3528
+ console.log( "Rule 3 matched: Defaulting to a new random UUID." );
3529
+ return 1;
3530
+ };
3531
+
3532
+ // Call the function with the fetched list of teams
3533
+ const defaultTeamId = findDefaultTeamId( teamsData );
3534
+
3535
+ // Assign the resulting ID to the finObject
3536
+ finObj.groupId = defaultTeamId;
3537
+
3538
+ // You can now use the 'defaultTeamId' variable and the updated userObject
3539
+ console.log( `The determined default Team ID is: ${defaultTeamId}` );
3540
+ }
3541
+
3542
+ } catch ( er ) {
3543
+
3544
+ let error = await errorService.handleError( er );
3545
+
3546
+ reject( {
3547
+
3548
+ error_message: "Dynamics365 Team Sync Error: Error occured while checking for default team in CX.",
3549
+ error_detail: error
3550
+ } );
3551
+ }
3552
+
3553
+ }
3554
+
3555
+ ( finObj?.type == "dynamics365" ) && ( finObj.groupName = "default" );
3556
+
3557
+ let ciscoTeamId = ( finObj?.type == "dynamics365" ) ? finObj.groupId : finObj.group.id;
3558
+
3559
+
3317
3560
  const { userTeam, supervisedTeams } = userTeams.data;
3318
3561
 
3319
3562
  let supervisedTeamsFiltered = [];
3320
3563
 
3321
- if ( supervisedTeams.length > 0 ) {
3564
+ if ( supervisedTeams.length > 0 && !( finObj?.type == "dynamics365" ) ) {
3322
3565
 
3323
3566
  //Fetching list of all primary and seconday supervised teams of current user (Whether in CX or Cisco)
3324
3567
  supervisedTeamsFiltered = supervisedTeams.filter( team => {
@@ -3339,7 +3582,7 @@ class KeycloakService extends Keycloak {
3339
3582
  }
3340
3583
 
3341
3584
  //If Agent team in finesse is different from Agent Team in finesse
3342
- if ( finObj.group.id !== userTeam.teamId ) {
3585
+ if ( ciscoTeamId !== userTeam.teamId ) {
3343
3586
 
3344
3587
  //We have to both add agent to a team corresponding to Finesse and remove it from CX team.
3345
3588
  //Removing agent from CX team first
@@ -3364,7 +3607,7 @@ class KeycloakService extends Keycloak {
3364
3607
  }
3365
3608
 
3366
3609
  //Check whether team of Agent already exists in CX Core or not
3367
- let URL4 = `${this.keycloakConfig[ "ef-server-url" ]}team?ids=${finObj.group.id}`;
3610
+ let URL4 = `${this.keycloakConfig[ "ef-server-url" ]}team?ids=${ciscoTeamId}`;
3368
3611
 
3369
3612
  config1.method = 'get';
3370
3613
  config1.url = URL4;
@@ -3383,8 +3626,8 @@ class KeycloakService extends Keycloak {
3383
3626
  let URL5 = `${this.keycloakConfig[ "ef-server-url" ]}team`;
3384
3627
 
3385
3628
  let data = {
3386
- "team_Id": finObj.group.id,
3387
- "team_name": finObj.group.name,
3629
+ "team_Id": ciscoTeamId,
3630
+ "team_name": ( finObj?.type == "dynamics365" ) ? finObj?.groupName : finObj.group.name,
3388
3631
  "supervisor_Id": "",
3389
3632
  "source": "CISCO",
3390
3633
  "created_by": "1"
@@ -3412,7 +3655,7 @@ class KeycloakService extends Keycloak {
3412
3655
  }
3413
3656
 
3414
3657
  //Assign Agent to a team
3415
- let URL6 = `${this.keycloakConfig[ "ef-server-url" ]}team/${finObj.group.id}/member`;
3658
+ let URL6 = `${this.keycloakConfig[ "ef-server-url" ]}team/${ciscoTeamId}/member`;
3416
3659
 
3417
3660
  data = {
3418
3661
  "type": "agent",
@@ -3451,7 +3694,7 @@ class KeycloakService extends Keycloak {
3451
3694
  }
3452
3695
 
3453
3696
  //If no team is assigned to supervise to current user in Cisco, remove its all supervised teams from CX
3454
- if ( !finObj.supervisedGroups && supervisedTeamsFiltered.length > 0 ) {
3697
+ if ( !finObj.supervisedGroups && supervisedTeamsFiltered.length > 0 && !( finObj?.type == "dynamics365" ) ) {
3455
3698
 
3456
3699
  for ( let supervisedTeam of supervisedTeamsFiltered ) {
3457
3700
 
@@ -3519,7 +3762,7 @@ class KeycloakService extends Keycloak {
3519
3762
 
3520
3763
  //Supervisor Case. Filtering out teams to add and teams to remove from Supervisor
3521
3764
  //First check that We have supervised Groups in finesse
3522
- if ( finObj.supervisedGroups ) {
3765
+ if ( finObj.supervisedGroups && !( finObj?.type == "dynamics365" ) ) {
3523
3766
 
3524
3767
  let finesseSupervisedGroups = finObj.supervisedGroups;
3525
3768
 
@@ -3769,6 +4012,151 @@ class KeycloakService extends Keycloak {
3769
4012
  } );
3770
4013
  }
3771
4014
 
4015
+ //AppDynamics365 SSO implementation
4016
+ async dynamics365Sso( userRoles, validationToken, dynamics365Url ) {
4017
+
4018
+ return new Promise( async ( resolve, reject ) => {
4019
+
4020
+ //Authentication of Finesse User, it returns a status code 200 if user found and 401 if unauthorized.
4021
+ let dynamics365LoginResponse = {};
4022
+
4023
+ try {
4024
+ //Handle finesse error cases correctly. (for later)
4025
+ if ( Object.keys( dynamics365LoginResponse ).length === 0 ) {
4026
+
4027
+ dynamics365LoginResponse = await dynamics365Service.authenticateUserViaDynamics365( validationToken, dynamics365Url );
4028
+
4029
+ }
4030
+
4031
+ //If user is SSO then password is not provided, we are setting up a pre-defined password.
4032
+ dynamics365LoginResponse.data.password = "123456";
4033
+
4034
+ let authenticatedByKeycloak = false;
4035
+ let keycloakAuthToken = null;
4036
+ let keycloakAdminToken = null;
4037
+ let updateUserPromise = null;
4038
+
4039
+
4040
+ if ( dynamics365LoginResponse?.status == 200 ) {
4041
+
4042
+ dynamics365LoginResponse.data.roles = userRoles;
4043
+ dynamics365LoginResponse.data.type = "dynamics365";
4044
+
4045
+ console.log( dynamics365LoginResponse.data.fullname );
4046
+
4047
+ let trimmedUsername = dynamics365LoginResponse?.data?.fullname.trim().split( ' ' )[ 0 ]; // Removes leading/trailing spaces
4048
+ dynamics365LoginResponse.data.fullname = trimmedUsername;
4049
+ console.log( dynamics365LoginResponse.data.fullname );
4050
+
4051
+ try {
4052
+
4053
+ //Fetching admin token, we pass it in our "Create User" API for authorization
4054
+ keycloakAdminToken = await this.getAccessToken( this.keycloakConfig[ "USERNAME_ADMIN" ], this.keycloakConfig[ "PASSWORD_ADMIN" ] );
4055
+
4056
+ try {
4057
+
4058
+ //Checking whether finesse user already exist in keycloak and fetch its token
4059
+ keycloakAuthToken = await this.getAccessToken( dynamics365LoginResponse?.data?.fullname, dynamics365LoginResponse.data.password, this.keycloakConfig[ "realm" ] );
4060
+ authenticatedByKeycloak = true;
4061
+
4062
+ if ( !updateUserPromise ) {
4063
+
4064
+ updateUserPromise = this.updateUser( dynamics365LoginResponse?.data, keycloakAdminToken, keycloakAuthToken, dynamics365LoginResponse?.data.username, dynamics365LoginResponse.data.password )
4065
+ .then( async ( updatedUser ) => {
4066
+
4067
+ //Calling the Introspect function twice so all the asynchronous operations inside updateUser function are done
4068
+ keycloakAuthToken = await this.getKeycloakTokenWithIntrospect( dynamics365LoginResponse?.data?.username, dynamics365LoginResponse.data.password, this.keycloakConfig[ "realm" ], 'CISCO' );
4069
+ } )
4070
+ .catch( ( err ) => {
4071
+
4072
+ reject( err );
4073
+ } );
4074
+ }
4075
+
4076
+
4077
+ } catch ( err ) {
4078
+
4079
+ if ( err.error_detail ) {
4080
+
4081
+ if ( err.error_detail.status == 401 ) {
4082
+
4083
+ console.log( "User Not Found in Keycloak: The user does not exist in Keycloak. Syncing Finesse user in Keycloak." );
4084
+ } else {
4085
+
4086
+ reject( err );
4087
+ }
4088
+ } else {
4089
+
4090
+ reject( err );
4091
+ }
4092
+
4093
+ }
4094
+ } catch ( err ) {
4095
+
4096
+ let error = await errorService.handleError( err );
4097
+
4098
+ reject( {
4099
+
4100
+ error_message: "Keycloak Admin Token Fetch Error: An error occurred while fetching the keycloak admin token in the authenticate/sync finesse user component.",
4101
+ error_detail: error
4102
+ } );
4103
+
4104
+
4105
+ } finally {
4106
+
4107
+ //Finesse User not found in keycloak, so we are going to create one.
4108
+ if ( !authenticatedByKeycloak ) {
4109
+
4110
+ if ( keycloakAdminToken.access_token ) {
4111
+
4112
+ try {
4113
+
4114
+ //Creating Finesse User inside keycloak.
4115
+ let userCreated = await this.createUser( dynamics365LoginResponse?.data, keycloakAdminToken.access_token );
4116
+
4117
+ if ( userCreated.status == 201 ) {
4118
+
4119
+ //Returning the token of recently created User
4120
+ keycloakAuthToken = await this.getKeycloakTokenWithIntrospect( ( dynamics365LoginResponse?.data?.fullname ).toLowerCase(), dynamics365LoginResponse.data.password, this.keycloakConfig[ "realm" ], 'CISCO' );
4121
+ }
4122
+
4123
+ } catch ( err ) {
4124
+
4125
+
4126
+ let error = await errorService.handleError( err );
4127
+
4128
+ reject( {
4129
+
4130
+ error_message: "Finesse User Creation Error: An error occurred while creating the finesse user in the authenticate/sync finesse user component.",
4131
+ error_detail: error
4132
+ } );
4133
+
4134
+ }
4135
+ }
4136
+ }
4137
+ }
4138
+
4139
+ if ( updateUserPromise ) {
4140
+ await updateUserPromise; // Wait for the updateUser promise to resolve
4141
+ updateUserPromise = null; // Reset the promise
4142
+ }
4143
+
4144
+ resolve( keycloakAuthToken );
4145
+ } else {
4146
+
4147
+ resolve( dynamics365LoginResponse );
4148
+ }
4149
+
4150
+
4151
+ } catch ( er ) {
4152
+
4153
+ reject( er );
4154
+ }
4155
+
4156
+
4157
+ } );
4158
+ }
4159
+
3772
4160
  //Sync implementation for teams along with its members (both create and update).
3773
4161
  async syncImplementation( finesseAdministratorUsername, finesseAdministratorPassword, finesseURL ) {
3774
4162
 
@@ -3790,34 +4178,34 @@ class KeycloakService extends Keycloak {
3790
4178
 
3791
4179
 
3792
4180
  /*
3793
-
3794
-
4181
+
4182
+
3795
4183
  workflow:
3796
-
4184
+
3797
4185
  call Cisco teams API to get all the teams, call CX Teams List API to get teams from CX. Compare both and see is there is any additional Cisco Team which doesn't exist on CX or CX team with type "CISCO" that doesn't eixst in Cisco. Create Cisco teams that are missing on CX and delete/disable CX teams that are missing on Cisco side. Also check if team_name of any team is updated on Cisco side and update it on CX. After that, call team detail API to fetch all the members of each team one by one. Then call then "CX API to get members of specific team" to get the members on CX side of same team. Check if there is any member missing or additional member of CX side and make the members exactly as in Cisco side. If a member doesn't exist then create it in keycloak and add it in CX Team corresponding to Cisco. After that, check the data of each member by calling "Keycloak API to get member list" having attribute: "Cisco User" and compare it with users in "Cisco API to fetch user list". if any user is additional in Cisco list then create it in CX and add it in its subsequent list. If any user is additional on keycloak side then disable that user. Similary, update the info exactly to the Cisco user if there is any difference i.e change in role, firstname, lastname, loginname, extension, team, supervised teams "represented by <teams> in user respresentation". There should be both create and update scenarios based on existance or non-existance of team and its members (agents, supervisors)
3798
-
4186
+
3799
4187
  Requirements:
3800
-
4188
+
3801
4189
  All cisco apis require administrator credentials
3802
-
4190
+
3803
4191
  Cisco API to fetch all teams: https://uccx12-5p.ucce.ipcc:8445/finesse/api/Teams?nocache=1680864072911&bypassServerCache=true
3804
4192
  Cisco API to fetch team detail: https://uccx12-5p.ucce.ipcc:8445/finesse/api/Team/8
3805
4193
  Cisco API to fetch user detail: https://uccx12-5p.ucce.ipcc:8445/finesse/api/User/SE2606
3806
4194
  Cisco API to fetch user list: https://uccx12-5p.ucce.ipcc:8445/finesse/api/Users
3807
-
4195
+
3808
4196
  CX APIs:
3809
-
4197
+
3810
4198
  CX API to get all teams: https://cxtd-qa05.expertflow.com/unified-admin/team
3811
4199
  CX API to get members of specific team: https://cxtd-qa05.expertflow.com/unified-admin/team/{teamId}/member?limit=25&offset=0
3812
-
4200
+
3813
4201
  Keycloak APIs:
3814
-
4202
+
3815
4203
  Keycloak API to get member list: https://cxtd-qa05.expertflow.com/auth/admin/realms/{realm}/users
3816
4204
  Keycloak API to create user: https://cxtd-qa05.expertflow.com/auth/admin/realms/{realm}/users
3817
4205
  Keycloak API to get details of user: https://cxtd-qa05.expertflow.com/auth/admin/realms/{realm}/users/{user-id}
3818
4206
  Keycloak API to get roles of user: https://cxtd-qa05.expertflow.com/auth/admin/realms/{realm}/roles
3819
4207
  Keycloak API to assign roles to user: https://cxtd-qa05.expertflow.com/auth/admin/realms/{realm}/users/${userId}/role-mappings/realm
3820
-
4208
+
3821
4209
  */
3822
4210
  }
3823
4211
 
@@ -4104,14 +4492,6 @@ class KeycloakService extends Keycloak {
4104
4492
 
4105
4493
  }
4106
4494
 
4107
- async createTeamsAndMembers() {
4108
-
4109
- }
4110
-
4111
- async updateTeamsAndMembers() {
4112
-
4113
- }
4114
-
4115
4495
  async checkPasswordUpdate( adminToken, userName, password ) {
4116
4496
 
4117
4497
  return new Promise( async ( resolve, reject ) => {
@@ -4277,53 +4657,19 @@ class KeycloakService extends Keycloak {
4277
4657
 
4278
4658
  // !-------------- Multitenancy -----------------!
4279
4659
 
4280
- async createRealmAsTenant( tenantName, realmDataString, authzConfigDataString, keycloakConfig ) {
4660
+ async createRealmAsTenant( tenantName, realmFile, authzConfigFilePath ) {
4281
4661
 
4282
4662
  return new Promise( async ( resolve, reject ) => {
4283
4663
 
4284
- let realmData;
4285
- let authzConfigData;
4286
-
4287
-
4288
- try {
4289
-
4290
- realmData = JSON.parse( realmDataString );
4291
-
4292
- } catch ( parseError ) {
4293
-
4294
- if ( parseError instanceof SyntaxError ) {
4295
-
4296
- reject( {
4297
- "error_message": "Error occurred while parsing Realm file during Tenant creation process.",
4298
- "error_detail": {
4299
- "status": 400,
4300
- "reason": `Invalid JSON in realm configuration file: ${parseError.message} `
4301
- }
4302
- } );
4303
- }
4304
-
4305
- reject( {
4306
- "error_message": "Error occurred while parsing Realm file during Tenant creation process.",
4307
- "error_detail": {
4308
- "status": 500,
4309
- "reason": `Error parsing realm configuration file: ${parseError.message}`
4310
- }
4311
- } );
4312
- }
4313
-
4314
- if ( Object.keys( realmData ).length < 1 ) {
4315
-
4316
- reject( {
4317
- errorStatus: 400,
4318
- errorMessage: `Received no realm data to import while creating tenant. Please send the correct realm data from realm file`
4319
- } );
4320
- }
4321
-
4322
4664
  let realmImportSuccessful = false;
4323
4665
  let mainMessage = "";
4666
+ let realmConfigString = '';
4667
+ let realmData = '';
4668
+ let authzConfigString = '';
4669
+ let authzConfig = '';
4324
4670
 
4325
4671
  let accessToken;
4326
- let URL = keycloakConfig[ "auth-server-url" ] + "realms/master/protocol/openid-connect/token";
4672
+ let URL = this.keycloakConfig[ "auth-server-url" ] + "realms/master/protocol/openid-connect/token";
4327
4673
 
4328
4674
  let config = {
4329
4675
  method: "post",
@@ -4334,8 +4680,8 @@ class KeycloakService extends Keycloak {
4334
4680
  data: {
4335
4681
  client_id: "admin-cli",
4336
4682
  grant_type: "password",
4337
- username: keycloakConfig[ "MASTER_USERNAME" ],
4338
- password: keycloakConfig[ "MASTER_PASSWORD" ]
4683
+ username: this.keycloakConfig[ "MASTER_USERNAME" ],
4684
+ password: this.keycloakConfig[ "MASTER_PASSWORD" ]
4339
4685
  },
4340
4686
  };
4341
4687
 
@@ -4345,10 +4691,76 @@ class KeycloakService extends Keycloak {
4345
4691
 
4346
4692
  accessToken = adminAccessToken.data.access_token;
4347
4693
 
4348
- let createRealmUrl = keycloakConfig[ "auth-server-url" ] + 'admin/realms';
4694
+ let createRealmUrl = this.keycloakConfig[ "auth-server-url" ] + 'admin/realms';
4349
4695
 
4350
4696
  // 1. Read the realm configuration JSON file
4351
- console.log( `Reading realm configuration from provided realm data.` );
4697
+ console.log( `Reading realm configuration from: ${realmFile}` );
4698
+
4699
+ try {
4700
+
4701
+ realmConfigString = await fs.readFileSync( realmFile, 'utf-8' );
4702
+
4703
+ } catch ( fileReadError ) {
4704
+
4705
+ if ( fileReadError.code === 'ENOENT' ) {
4706
+
4707
+ reject( {
4708
+ "error_message": "Error occurred while reading Realm file during Tenant creation process.",
4709
+ "error_detail": {
4710
+ "status": 404,
4711
+ "reason": `Realm configuration file not found at provided path: ${realmFile} `
4712
+ }
4713
+ } );
4714
+ }
4715
+
4716
+ reject( {
4717
+ "error_message": "Error occurred while reading Realm file during Tenant creation process.",
4718
+ "error_detail": {
4719
+ "status": 500,
4720
+ "reason": `Error reading realm configuration file: ${fileReadError.message}. {file path: ${realmFile}} `
4721
+ }
4722
+ } )
4723
+
4724
+ }
4725
+
4726
+ if ( !realmConfigString || realmConfigString.trim() === '' ) {
4727
+
4728
+ reject( {
4729
+ "error_message": "Error occurred while reading Realm file during Tenant creation process.",
4730
+ "error_detail": {
4731
+ "status": 404,
4732
+ "reason": `Provided Realm configuration file to create Tenant is empty. Please provide valid realm file in JSON format. `
4733
+ }
4734
+ } );
4735
+
4736
+ }
4737
+
4738
+ try {
4739
+
4740
+ realmData = JSON.parse( realmConfigString );
4741
+
4742
+ } catch ( parseError ) {
4743
+
4744
+ if ( parseError instanceof SyntaxError ) {
4745
+
4746
+ reject( {
4747
+ "error_message": "Error occurred while parsing Realm file during Tenant creation process.",
4748
+ "error_detail": {
4749
+ "status": 400,
4750
+ "reason": `Invalid JSON in realm configuration file: ${parseError.message} `
4751
+ }
4752
+ } );
4753
+ }
4754
+
4755
+ reject( {
4756
+ "error_message": "Error occurred while parsing Realm file during Tenant creation process.",
4757
+ "error_detail": {
4758
+ "status": 500,
4759
+ "reason": `Error parsing realm configuration file: ${parseError.message}`
4760
+ }
4761
+ } );
4762
+ }
4763
+
4352
4764
 
4353
4765
  realmData.id = tenantName;
4354
4766
  realmData.realm = tenantName;
@@ -4376,10 +4788,8 @@ class KeycloakService extends Keycloak {
4376
4788
 
4377
4789
  }
4378
4790
 
4379
- console.log( mainMessage );
4380
-
4381
4791
  // --- Authorization Settings Import (if applicable) ---
4382
- if ( realmImportSuccessful && authzConfigDataString ) {
4792
+ if ( realmImportSuccessful && authzConfigFilePath ) {
4383
4793
 
4384
4794
  let targetClientIdForAuthz = 'cim';
4385
4795
 
@@ -4390,7 +4800,7 @@ class KeycloakService extends Keycloak {
4390
4800
 
4391
4801
  // 4. Get the internal UUID of the target client
4392
4802
  console.log( `Fetching UUID for client '${targetClientIdForAuthz}' in realm '${tenantName}'...` );
4393
- const getClientUrl = `${keycloakConfig[ "auth-server-url" ]}admin/realms/${tenantName}/clients`;
4803
+ const getClientUrl = `${this.keycloakConfig[ "auth-server-url" ]}admin/realms/${tenantName}/clients`;
4394
4804
 
4395
4805
  let config2 = {
4396
4806
 
@@ -4425,13 +4835,51 @@ class KeycloakService extends Keycloak {
4425
4835
  throw new Error( `Client with clientId '${targetClientIdForAuthz}' not found in realm '${tenantName}'. Response: ${JSON.stringify( clientSearchResponse.data )}` );
4426
4836
  }
4427
4837
 
4838
+ // 5. Read the authorization settings JSON file
4839
+ console.log( `Reading authorization settings from: ${authzConfigFilePath}` );
4428
4840
 
4429
- // 6. Make the API call to Keycloak to import/update authorization settings
4430
- console.log( `Importing authorization settings for client UUID '${clientUuid}'...` );
4841
+ try {
4842
+
4843
+ authzConfigString = await fs.readFileSync( authzConfigFilePath, 'utf-8' );
4844
+
4845
+ } catch ( fileReadError ) {
4846
+
4847
+ if ( fileReadError.code === 'ENOENT' ) {
4848
+
4849
+ reject( {
4850
+ "error_message": `Error occurred while reading Authz file while importing Permissions/Policies in ${targetClientIdForAuthz} during Tenant creation process.`,
4851
+ "error_detail": {
4852
+ "status": 404,
4853
+ "reason": `Authz file not found at provided path: ${authzConfigString}. `
4854
+ }
4855
+ } );
4856
+ }
4857
+
4858
+ reject( {
4859
+ "error_message": `Error occurred while reading Authz file while importing Permissions/Policies in ${targetClientIdForAuthz} during Tenant creation process.`,
4860
+ "error_detail": {
4861
+ "status": 500,
4862
+ "reason": `Error reading authz file: ${fileReadError.message}. {file path: ${authzConfigString}} `
4863
+ }
4864
+ } )
4865
+
4866
+ }
4867
+
4868
+ if ( !authzConfigString || authzConfigString.trim() === '' ) {
4869
+
4870
+ reject( {
4871
+ "error_message": `Error occurred while reading Authz file while importing Permissions/Policies in ${targetClientIdForAuthz} during Tenant creation process.`,
4872
+ "error_detail": {
4873
+ "status": 404,
4874
+ "reason": `Provided authz file to import Permissions/Policies is empty. Please provide valid authz file in JSON format. `
4875
+ }
4876
+ } );
4877
+
4878
+ }
4431
4879
 
4432
4880
  try {
4433
4881
 
4434
- authzConfigData = JSON.parse( authzConfigDataString );
4882
+ authzConfig = JSON.parse( authzConfigString );
4435
4883
 
4436
4884
  } catch ( parseError ) {
4437
4885
 
@@ -4455,15 +4903,11 @@ class KeycloakService extends Keycloak {
4455
4903
  } );
4456
4904
  }
4457
4905
 
4458
- if ( Object.keys( authzConfigData ).length < 1 ) {
4459
4906
 
4460
- reject( {
4461
- errorStatus: 400,
4462
- errorMessage: `Received no authorization data to import while creating tenant. Please send the correct authorization data from authz file`
4463
- } );
4464
- }
4907
+ // 6. Make the API call to Keycloak to import/update authorization settings
4908
+ console.log( `Importing authorization settings for client UUID '${clientUuid}'...` );
4465
4909
 
4466
- const importAuthzUrl = `${keycloakConfig[ "auth-server-url" ]}admin/realms/${tenantName}/clients/${clientUuid}/authz/resource-server/import`;
4910
+ const importAuthzUrl = `${this.keycloakConfig[ "auth-server-url" ]}admin/realms/${tenantName}/clients/${clientUuid}/authz/resource-server/import`;
4467
4911
 
4468
4912
  let config3 = {
4469
4913
 
@@ -4473,7 +4917,7 @@ class KeycloakService extends Keycloak {
4473
4917
  "Content-Type": "application/json",
4474
4918
  "Authorization": `Bearer ${accessToken}`
4475
4919
  },
4476
- data: authzConfigData
4920
+ data: authzConfig
4477
4921
  };
4478
4922
 
4479
4923
  const authzResponse = await requestController.httpRequest( config3, false );
@@ -4521,6 +4965,10 @@ class KeycloakService extends Keycloak {
4521
4965
  console.error( 'No response received from Keycloak (Authz Import):', er.request );
4522
4966
 
4523
4967
 
4968
+ } else if ( er?.code === 'ENOENT' && er?.path === authzConfigFilePath ) {
4969
+
4970
+ console.error( 'Authorization settings file not found:', authzConfigFilePath );
4971
+
4524
4972
  } else {
4525
4973
 
4526
4974
  console.error( 'Error during authz import request setup or client lookup:', er?.message );