Skip to content

Commit 8e13e84

Browse files
authored
Merge pull request #385 from bcgov/fix/delete-limit
fix: deletion limit
2 parents 919cc14 + 4b4704d commit 8e13e84

File tree

2 files changed

+139
-10
lines changed

2 files changed

+139
-10
lines changed

docker/kc-cron-job/remove-inactive-idir-users.js

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
const _ = require('lodash');
22
const async = require('async');
3+
const { promisify } = require('util');
34
const axios = require('axios');
5+
const { parseString } = require('xml2js');
46
const { getAdminClient, log, getPgClient, sendRcNotification, handleError, deleteLegacyData } = require('./helpers');
57
const jwt = require('jsonwebtoken');
68
const { ConfidentialClientApplication } = require('@azure/msal-node');
@@ -10,6 +12,9 @@ const MS_GRAPH_IDIR_GUID_ATTRIBUTE = 'onPremisesExtensionAttributes/extensionAtt
1012

1113
require('dotenv').config();
1214

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+
1318
let devMsalInstance;
1419
let testMsalInstance;
1520
let prodMsalInstance;
@@ -85,7 +90,11 @@ async function getAzureAccessToken(env) {
8590
}
8691
}
8792

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 }) {
8998
try {
9099
const accessToken = await getAzureAccessToken(env);
91100
const options = {
@@ -111,6 +120,124 @@ async function checkUserExistsAtIDIM({ property = MS_GRAPH_IDIR_GUID_ATTRIBUTE,
111120
}
112121
}
113122

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+
114241
async function getUserRolesMappings(adminClient, userId) {
115242
try {
116243
const clientRoles = [];
@@ -205,6 +332,8 @@ async function removeStaleUsersByEnv(env = 'dev', pgClient, runnerName, startFro
205332
await pgClient.query({ text, values });
206333
deletedUserCount++;
207334
log(`[${runnerName}] ${username} has been deleted from ${env} environment`);
335+
336+
if (deletedUserCount > MAX_DELETED_USERS_PER_RUNNER) break;
208337
} else continue;
209338
}
210339
}
@@ -213,7 +342,7 @@ async function removeStaleUsersByEnv(env = 'dev', pgClient, runnerName, startFro
213342
if (count < max || total === 10000) break;
214343

215344
// 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;
217346

218347
await adminClient.reauth();
219348
first = first + max;

docker/kc-cron-job/yarn.lock

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@
1515
"@jridgewell/gen-mapping" "^0.3.0"
1616
"@jridgewell/trace-mapping" "^0.3.9"
1717

18-
"@azure/msal-common@14.12.0":
19-
version "14.12.0"
20-
resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-14.12.0.tgz#844abe269b071f8fa8949dadc2a7b65bbb147588"
21-
integrity sha512-IDDXmzfdwmDkv4SSmMEyAniJf6fDu3FJ7ncOjlxkDuT85uSnLEhZi3fGZpoR7T4XZpOMx9teM9GXBgrfJgyeBw==
18+
"@azure/msal-common@14.15.0":
19+
version "14.15.0"
20+
resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-14.15.0.tgz#0e27ac0bb88fe100f4f8d1605b64d5c268636a55"
21+
integrity sha512-ImAQHxmpMneJ/4S8BRFhjt1MZ3bppmpRPYYNyzeQPeFN288YKbb8TmmISQEbtfkQ1BPASvYZU5doIZOPBAqENQ==
2222

2323
"@azure/msal-node@^2.9.2":
24-
version "2.9.2"
25-
resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-2.9.2.tgz#e6d3c1661012c1bd0ef68e328f73a2fdede52931"
26-
integrity sha512-8tvi6Cos3m+0KmRbPjgkySXi+UQU/QiuVRFnrxIwt5xZlEEFa69O04RTaNESGgImyBBlYbo2mfE8/U8Bbdk1WQ==
24+
version "2.14.0"
25+
resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-2.14.0.tgz#7881895d41b03d8b9b38a29550ba3bbb15f73b3c"
26+
integrity sha512-rrfzIpG3Q1rHjVYZmHAEDidWAZZ2cgkxlIcMQ8dHebRISaZ2KCV33Q8Vs+uaV6lxweROabNxKFlR2lIKagZqYg==
2727
dependencies:
28-
"@azure/msal-common" "14.12.0"
28+
"@azure/msal-common" "14.15.0"
2929
jsonwebtoken "^9.0.0"
3030
uuid "^8.3.0"
3131

0 commit comments

Comments
 (0)