1
1
const _ = require ( 'lodash' ) ;
2
2
const async = require ( 'async' ) ;
3
+ const { promisify } = require ( 'util' ) ;
3
4
const axios = require ( 'axios' ) ;
5
+ const { parseString } = require ( 'xml2js' ) ;
4
6
const { getAdminClient, log, getPgClient, sendRcNotification, handleError, deleteLegacyData } = require ( './helpers' ) ;
5
7
const jwt = require ( 'jsonwebtoken' ) ;
6
8
const { ConfidentialClientApplication } = require ( '@azure/msal-node' ) ;
@@ -10,6 +12,9 @@ const MS_GRAPH_IDIR_GUID_ATTRIBUTE = 'onPremisesExtensionAttributes/extensionAtt
10
12
11
13
require ( 'dotenv' ) . config ( ) ;
12
14
15
+ // NOTE: this is per runner, e.g with 5 in prod 50 is the total user deletion limit
16
+ const MAX_DELETED_USERS_PER_RUNNER = 30 ;
17
+
13
18
let devMsalInstance ;
14
19
let testMsalInstance ;
15
20
let prodMsalInstance ;
@@ -85,7 +90,11 @@ async function getAzureAccessToken(env) {
85
90
}
86
91
}
87
92
88
- async function checkUserExistsAtIDIM ( { property = MS_GRAPH_IDIR_GUID_ATTRIBUTE , matchKey = '' , env } ) {
93
+ /*
94
+ This function checks existence using MS Graph. Currently has issues with being out of sync with IDIM, so is unused.
95
+ Keeping the function in case the sync issue can be resolved.
96
+ */
97
+ async function checkUserExistsAtEntra ( { property = MS_GRAPH_IDIR_GUID_ATTRIBUTE , matchKey = '' , env } ) {
89
98
try {
90
99
const accessToken = await getAzureAccessToken ( env ) ;
91
100
const options = {
@@ -111,6 +120,124 @@ async function checkUserExistsAtIDIM({ property = MS_GRAPH_IDIR_GUID_ATTRIBUTE,
111
120
}
112
121
}
113
122
123
+ const parseStringSync = promisify ( parseString ) ;
124
+
125
+ function getWebServiceInfo ( { env = 'dev' } ) {
126
+ const requestHeaders = {
127
+ 'Content-Type' : 'text/xml;charset=UTF-8' ,
128
+ authorization : `Basic ${ process . env . BCEID_SERVICE_BASIC_AUTH } `
129
+ } ;
130
+
131
+ const requesterIdirGuid = process . env . BCEID_REQUESTER_IDIR_GUID || '' ;
132
+
133
+ let serviceUrl = '' ;
134
+ let serviceId = '' ;
135
+ if ( env === 'dev' ) {
136
+ serviceUrl = 'https://gws2.development.bceid.ca' ;
137
+ serviceId = process . env . BCEID_SERVICE_ID_DEV || '' ;
138
+ } else if ( env === 'test' ) {
139
+ serviceUrl = 'https://gws2.test.bceid.ca' ;
140
+ serviceId = process . env . BCEID_SERVICE_ID_TEST || '' ;
141
+ } else if ( env === 'prod' ) {
142
+ serviceUrl = 'https://gws2.bceid.ca' ;
143
+ serviceId = process . env . BCEID_SERVICE_ID_PROD || '' ;
144
+ }
145
+
146
+ return { requestHeaders, requesterIdirGuid, serviceUrl, serviceId } ;
147
+ }
148
+
149
+ const generateXML = (
150
+ {
151
+ property = 'userId' ,
152
+ matchKey = '' ,
153
+ matchType = 'Exact' ,
154
+ serviceId = '' ,
155
+ requesterIdirGuid = '' ,
156
+ page = 1 ,
157
+ limit = 1
158
+ } ,
159
+ requestType = 'searchInternalAccount'
160
+ ) => {
161
+ if ( requestType === 'getAccountDetail' ) {
162
+ return `<?xml version="1.0" encoding="UTF-8"?>
163
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:V10="http://www.bceid.ca/webservices/Client/V10/">
164
+ <soapenv:Header />
165
+ <soapenv:Body>
166
+ <V10:getAccountDetail>
167
+ <V10:accountDetailRequest>
168
+ <V10:onlineServiceId>${ serviceId } </V10:onlineServiceId>
169
+ <V10:requesterAccountTypeCode>Internal</V10:requesterAccountTypeCode>
170
+ <V10:requesterUserGuid>${ requesterIdirGuid } </V10:requesterUserGuid>
171
+ <V10:${ property } >${ matchKey } </V10:${ property } >
172
+ <V10:accountTypeCode>Internal</V10:accountTypeCode>
173
+ </V10:accountDetailRequest>
174
+ </V10:getAccountDetail>
175
+ </soapenv:Body>
176
+ </soapenv:Envelope>` ;
177
+ } else {
178
+ return `<?xml version="1.0" encoding="UTF-8"?>
179
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:V10="http://www.bceid.ca/webservices/Client/V10/">
180
+ <soapenv:Header />
181
+ <soapenv:Body>
182
+ <V10:searchInternalAccount>
183
+ <V10:internalAccountSearchRequest>
184
+ <V10:onlineServiceId>${ serviceId } </V10:onlineServiceId>
185
+ <V10:requesterAccountTypeCode>Internal</V10:requesterAccountTypeCode>
186
+ <V10:requesterUserGuid>${ requesterIdirGuid } </V10:requesterUserGuid>
187
+ <requesterAccountTypeCode>Internal</requesterAccountTypeCode>
188
+ <V10:pagination>
189
+ <V10:pageSizeMaximum>${ String ( limit || 100 ) } </V10:pageSizeMaximum>
190
+ <V10:pageIndex>${ String ( page || 1 ) } </V10:pageIndex>
191
+ </V10:pagination>
192
+ <V10:sort>
193
+ <V10:direction>Ascending</V10:direction>
194
+ <V10:onProperty>UserId</V10:onProperty>
195
+ </V10:sort>
196
+ <V10:accountMatch>
197
+ <V10:${ property } >
198
+ <V10:value>${ matchKey } </V10:value>
199
+ <V10:matchPropertyUsing>${ matchType } </V10:matchPropertyUsing>
200
+ </V10:${ property } >
201
+ </V10:accountMatch>
202
+ </V10:internalAccountSearchRequest>
203
+ </V10:searchInternalAccount>
204
+ </soapenv:Body>
205
+ </soapenv:Envelope>` ;
206
+ }
207
+ } ;
208
+
209
+ async function checkUserExistsAtIDIM ( { property = 'userGuid' , matchKey = '' , env = 'prod' } ) {
210
+ const { requestHeaders, requesterIdirGuid, serviceUrl, serviceId } = getWebServiceInfo ( { env } ) ;
211
+ const xml = generateXML ( { property, matchKey, serviceId, requesterIdirGuid } , 'getAccountDetail' ) ;
212
+
213
+ try {
214
+ const response = await axios . post ( `${ serviceUrl } /webservices/client/V10/BCeIDService.asmx?WSDL` , xml , {
215
+ headers : requestHeaders ,
216
+ timeout : 10000
217
+ } ) ;
218
+
219
+ const { data : body } = response ;
220
+
221
+ const result = await parseStringSync ( body ) ;
222
+ const data = _ . get ( result , 'soap:Envelope.soap:Body.0.getAccountDetailResponse.0.getAccountDetailResult.0' ) ;
223
+ if ( ! data ) throw Error ( 'no data' ) ;
224
+
225
+ const status = _ . get ( data , 'code.0' ) ;
226
+ const failureCode = _ . get ( data , 'failureCode.0' ) ;
227
+ const failMessage = _ . get ( data , 'message.0' ) ;
228
+ if ( status === 'Success' && failureCode === 'Void' ) {
229
+ return 'exists' ;
230
+ } else if ( status === 'Failed' && failureCode === 'NoResults' ) {
231
+ return 'notexists' ;
232
+ } else {
233
+ log ( `${ env } : [${ status } ][${ failureCode } ] ${ property } : ${ matchKey } : ${ String ( failMessage ) } )` ) ;
234
+ }
235
+ return 'error' ;
236
+ } catch ( error ) {
237
+ throw new Error ( error ) ;
238
+ }
239
+ }
240
+
114
241
async function getUserRolesMappings ( adminClient , userId ) {
115
242
try {
116
243
const clientRoles = [ ] ;
@@ -205,6 +332,8 @@ async function removeStaleUsersByEnv(env = 'dev', pgClient, runnerName, startFro
205
332
await pgClient . query ( { text, values } ) ;
206
333
deletedUserCount ++ ;
207
334
log ( `[${ runnerName } ] ${ username } has been deleted from ${ env } environment` ) ;
335
+
336
+ if ( deletedUserCount > MAX_DELETED_USERS_PER_RUNNER ) break ;
208
337
} else continue ;
209
338
}
210
339
}
@@ -213,7 +342,7 @@ async function removeStaleUsersByEnv(env = 'dev', pgClient, runnerName, startFro
213
342
if ( count < max || total === 10000 ) break ;
214
343
215
344
// max 50 users can be deleted by a runner at a time
216
- if ( deletedUserCount > 50 ) break ;
345
+ if ( deletedUserCount > MAX_DELETED_USERS_PER_RUNNER ) break ;
217
346
218
347
await adminClient . reauth ( ) ;
219
348
first = first + max ;
0 commit comments