Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Js ofmcc 2 provider login3 #21

Merged
merged 19 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

248 changes: 75 additions & 173 deletions backend/src/components/user.js
Original file line number Diff line number Diff line change
@@ -1,236 +1,138 @@
'use strict';
const {getSessionUser, getHttpHeader, minify, getUserGuid, getUserName, getLabelFromValue, postOperation, isIdirUser, getOperation} = require('./utils');
const config = require('../config/index');
const ApiError = require('./error');
const axios = require('axios');
const HttpStatus = require('http-status-codes');
const log = require('../components/logger');
const { APPLICATION_STATUS_CODES, CCFRI_STATUS_CODES, ECEWE_STATUS_CODES, CCOF_STATUS_CODES, CCOF_APPLICATION_TYPES, ORGANIZATION_PROVIDER_TYPES, CHANGE_REQUEST_TYPES} = require('../util/constants');
const { UserProfileFacilityMappings, UserProfileOrganizationMappings, UserProfileBaseFundingMappings, UserProfileApplicationMappings, UserProfileCCFRIMappings, UserProfileECEWEMappings /* lint error: , UserProfileChangeRequestNewFacilityMappings*/} = require('../util/mapping/Mappings');

const { MappableObjectForFront } = require('../util/mapping/MappableObject');
const _ = require ('lodash');

'use strict'
const { getSessionUser, getUserName, getHttpHeader, minify, getUserGuid, isIdirUser } = require('./utils')
const config = require('../config/index')
const ApiError = require('./error')
const axios = require('axios')
const HttpStatus = require('http-status-codes')
const log = require('../components/logger')
// TODO... const { ORGANIZATION_PROVIDER_TYPES} = require('../util/constants')
const { UserProfileMappings, UserProfileOrganizationMappings, UserProfileFacilityPermissionMappings, UserProfileFacilityMappings } = require('../util/mapping/Mappings')

const { MappableObjectForFront } = require('../util/mapping/MappableObject')
const _ = require('lodash')

async function getUserInfo(req, res) {

const userInfo = getSessionUser(req);
const userInfo = getSessionUser(req)
if (!userInfo || !userInfo.jwt || !userInfo._json) {
return res.status(HttpStatus.UNAUTHORIZED).json({
message: 'No session data'
});
message: 'No session data',
})
}
const isIdir = isIdirUser(req);
const queryUserName = req.params?.queryUserName;
const userName = getUserName(req);
const isIdir = isIdirUser(req)
const queryUserName = req.params?.queryUserName
const userName = getUserName(req)

// if is idir user (ministry user), make sure they are a user in dynamics
// TODO commented out until we focus on IDIR login and weather this code is relevant
if (isIdir) {
let response = await getDynamicsUserByEmail(req);
let response = await getDynamicsUserByEmail(req)
if (response.value?.length > 0 && response.value[0].systemuserid) {
log.verbose(`Ministry user: [${req.session.passport.user._json.display_name}] logged in.`);
log.verbose(`Ministry user: [${req.session.passport.user._json.display_name}] logged in.`)
} else {
log.info(`Ministry user: [${req.session.passport.user._json.display_name}] attempted to log in but is not part of Dynamics.`);
log.info(`Ministry user: [${req.session.passport.user._json.display_name}] attempted to log in but is not part of Dynamics.`)
return res.status(HttpStatus.UNAUTHORIZED).json({
message: 'Not Authorized'
});
message: 'Not Authorized',
})
}
}
let resData = {
displayName: (queryUserName)? userName + '-' + queryUserName : userName,
// TODO i thing this has to do with impersonate... displayName: (queryUserName)? userName + '-' + queryUserName : userName,
userName: userName,
email: req.session.passport.user._json.email,
isMinistryUser: isIdir,
serverTime: new Date(),
//TODO: unreadMessages is hardcoded. Remove this with API values when built out!
unreadMessages: false,
};
let userResponse = undefined;
}
let userResponse = undefined
if (isIdir) {
if (queryUserName) {
try {
log.info(`Ministry user [${userName}] is impersonating with username: [${queryUserName}].`);
log.info(`Ministry user [${userName}] is impersonating with username: [${queryUserName}].`)
// dynamics api requires a userID. if userID not found then it wil use the query name
// put a random userID so that we only search by queryname
userResponse = await getUserProfile(null, queryUserName);
userResponse = await getUserProfile(null, queryUserName)
if (userResponse === null) {
return res.status(HttpStatus.NOT_FOUND).json({message: 'No user found with that BCeID UserName'});
return res.status(HttpStatus.NOT_FOUND).json({ message: 'No user found with that BCeID UserName' })
}
} catch (e) {
log.error('getUserProfile Error', e.response ? e.response.status : e.message);
throw new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, {message: 'API Get error'}, e);
log.error('getUserProfile Error', e.response ? e.response.status : e.message)
throw new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, { message: 'API Get error' }, e)
}
} else {
//If not looking for a username, return from here since ministry staff should not have an account
return res.status(HttpStatus.OK).json(resData);
return res.status(HttpStatus.OK).json(resData)
}
} else {
//Not an idir user, so just get the guid from the header
const userGuid = getUserGuid(req);
log.verbose('User Guid is: ', userGuid);
userResponse = await getUserProfile(userGuid, userName );
const userGuid = getUserGuid(req)
log.verbose('User Guid is: ', userGuid)
userResponse = await getUserProfile(userGuid)
}

if (log.isVerboseEnabled) {
log.verbose('getUserProfile response:',minify(userResponse));
log.verbose('getUserProfile response:', minify(userResponse))
}

if (userResponse === null) {
creatUser(req);
return res.status(HttpStatus.OK).json(resData);
}
if (userResponse === {}){
// If no data back, then no associated Organization/Facilities, return empty orgination data
return res.status(HttpStatus.OK).json(resData);
// If no data back, then no associated User Roles/Organization/Facilities/
return res.status(HttpStatus.UNAUTHORIZED).json(resData)
}

let organization = new MappableObjectForFront(userResponse, UserProfileOrganizationMappings).data;
let user = new MappableObjectForFront(userResponse, UserProfileMappings).data
let organization = new MappableObjectForFront(userResponse.organization, UserProfileOrganizationMappings).data
resData.facilityPermission = parseFacilityPermissions(userResponse)

let application = new MappableObjectForFront(userResponse.application, UserProfileApplicationMappings).data;
application.organizationProviderType = getLabelFromValue(application.organizationProviderType, ORGANIZATION_PROVIDER_TYPES);
application.applicationStatus = getLabelFromValue(application.applicationStatus, APPLICATION_STATUS_CODES, 'NEW');
application.applicationType = getLabelFromValue(application.applicationType, CCOF_APPLICATION_TYPES);
application.ccofProgramYearId = userResponse.application?.ccof_ProgramYear?.ccof_program_yearid;
application.ccofProgramYearName = userResponse.application?.ccof_ProgramYear?.ccof_name;
application.ccofApplicationStatus = getLabelFromValue(application.ccofStatus, CCOF_STATUS_CODES, 'NEW');


resData.facilityList = parseFacilityData(userResponse);
let results = {
...resData,
...user,
...organization,
...application,
};
return res.status(HttpStatus.OK).json(results);
}
log.verbose('getUserInfo response:', results)
return res.status(HttpStatus.OK).json(results)
}

async function getUserProfile(userGuid, userName) {
async function getUserProfile(userGuid) {
try {
let url = undefined;
let url = undefined
if (userGuid) {
url = config.get('dynamicsApi:apiEndpoint') + `/api/ProviderProfile?userId=${userGuid}&userName=${userName}`;
} else {
url = config.get('dynamicsApi:apiEndpoint') + `/api/ProviderProfile?userName=${userName}`;
url = config.get('dynamicsApi:apiEndpoint') + `/api/ProviderProfile?userId=${userGuid}`
}

log.verbose('UserProfile Url is', url);
const response = await axios.get(url, getHttpHeader());
return response.data;
log.verbose('UserProfile Url is', url)
let response = undefined
response = await axios.get(url, getHttpHeader())
log.verbose('getUserProfile response:', response.data)
return response.data
} catch (e) {
if (e.response?.status == '404') {
log.verbose('response ', e.response.data);
log.verbose('response ', e.response.data)
if (e.response?.data?.startsWith('User not found')) {
return null;
return null
}
return {};
return {}
}
log.error('getUserProfile Error', e.response ? e.response.status : e.message);
throw e;
log.error('getUserProfile Error', e.response ? e.response.status : e.message)
throw e
}
}

function updateFacilityWithChangeRequestDetails(changeRequestList, returnValue, facilityId) {
for (const changeRequest of changeRequestList) {
//todo -mk check statuscode
let changeActionNewFacilityList = changeRequest?.ccof_change_action_change_request?.filter(item =>item.ccof_changetype === CHANGE_REQUEST_TYPES.NEW_FACILITY);
for (const changeActionNewFacility of changeActionNewFacilityList) {
let result = changeActionNewFacility?.ccof_change_request_new_facility_change_act.find(item => item['_ccof_facility_value'] === facilityId);
if (result) {
returnValue.changeRequestId = changeRequest?.ccof_change_requestid;
returnValue.unlockCcfri = result?.ccof_unlock_ccfri;
returnValue.unlockNmf = result?.ccof_unlock_nmf_rfi;
returnValue.unlockRfi = result?.ccof_unlock_rfi;

function parseFacilityPermissions(userResponse) {
const facilityList = Object.entries(userResponse.facility_permission)
.map(([key, value]) => {
// Only add facilities that have portal access
if (value.ofm_portal_access === true) {
const facilityPermission = new MappableObjectForFront(value, UserProfileFacilityPermissionMappings).data
const facility = new MappableObjectForFront(value.facility, UserProfileFacilityMappings).data
const combinedData = { ...facilityPermission, ...facility }
if (!_.isEmpty(combinedData)) {
return combinedData
}
}
}
}
}

function parseFacilityData(userResponse) {
let facilityMap = new Map(userResponse.facilities?.map((m) => [m['accountid'], new MappableObjectForFront(m, UserProfileFacilityMappings).data]));

if (userResponse.application) {
facilityMap.forEach((value, key, map) => {
let ccfriInfo = userResponse.application.ccof_applicationccfri_Application_ccof_ap?.find(item => item['_ccof_facility_value'] === key);
ccfriInfo = new MappableObjectForFront(ccfriInfo, UserProfileCCFRIMappings).data;
let eceweInfo = userResponse.application.ccof_ccof_application_ccof_applicationecewe_application?.find(item => item['_ccof_facility_value'] === key);
eceweInfo = new MappableObjectForFront(eceweInfo, UserProfileECEWEMappings).data;
let baseFunding = userResponse.application.ccof_application_basefunding_Application?.find(item => item['_ccof_facility_value'] === key);
baseFunding = new MappableObjectForFront(baseFunding, UserProfileBaseFundingMappings).data;
let changeRequestList = userResponse.application.ccof_ccof_change_request_Application_ccof_appl;
let returnValue = {
...value,
...ccfriInfo,
...eceweInfo,
...baseFunding,
};
updateFacilityWithChangeRequestDetails(changeRequestList, returnValue, key);
map.set(key, returnValue);
});
}
let facilityList = [];
facilityMap.forEach((facility) => {
if (!_.isEmpty(facility)) {
facility.ccofBaseFundingStatus = getLabelFromValue(facility.ccofBaseFundingStatus, CCOF_STATUS_CODES);
facility.ccfriStatus = getLabelFromValue(facility.ccfriStatus, CCFRI_STATUS_CODES, 'NOT STARTED');
facility.eceweStatus = getLabelFromValue(facility.eceweStatus, ECEWE_STATUS_CODES, 'NOT STARTED');
facilityList.push(facility);
}
});
return facilityList;
}

async function getDynamicsUserByEmail(req) {
let email = req.session.passport.user._json.email;
if (!email) {
//If for some reason, an email is not associated with the IDIR, just use [email protected]
email = `${req.session.passport.user._json.idir_username}@gov.bc.ca`;
}
// eslint-disable-next-line quotes,
email.includes("'") ? email = email.replace("'", "''") : email;
try {
let response = await getOperation(`systemusers?$select=firstname,domainname,lastname&$filter=internalemailaddress eq '${email}'`);
return response;
} catch (e) {
log.error('getDynamicsUserByEmail Error', e.response ? e.response.status : e.message);
throw new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, {message: 'API Get error'}, e);
}
}

async function creatUser(req) {
log.info('No user found, creating BCeID User: ', getUserName(req));
let given_name = req.session.passport.user._json.given_name;
let family_name = req.session.passport.user._json.family_name;
let firstname = undefined;
let lastname = undefined;
try {
if (!family_name && given_name && given_name.split(' ').length > 1) {
//If for some reason we don't have a last name from SSO, see if firstname has 2 words
firstname = given_name.split(' ').slice(0, -1).join(' ');
lastname = given_name.split(' ').slice(-1).join(' ');
} else if (!given_name && family_name && family_name.split(' ').length > 1) {
//If for some reason we don't have a firstname name from SSO, see if lastname has 2 words
firstname = family_name.split(' ').slice(0, -1).join(' ');
lastname = family_name.split(' ').slice(-1).join(' ');
} else {
firstname = given_name;
lastname = family_name;
}

let payload = {
ccof_userid: getUserGuid(req),
firstname: firstname,
lastname: lastname,
emailaddress1: req.session.passport.user._json.email,
ccof_username: getUserName(req)
};
postOperation('contacts', payload);
} catch (e) {
log.error('Error when creating user: ', e);
throw new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, {message: 'Error while creating a new BCeID User'}, e);
}
return null
})
.filter((facility) => facility !== null)
return facilityList
}

module.exports = {
getUserInfo,
};
}
65 changes: 35 additions & 30 deletions backend/src/util/mapping/Mappings.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,39 @@
const OrganizationMappings = [
{ back: 'ccof_facilitystartdate', front: 'yearBeganOperation' },
{ back: 'name', front: 'legalName' },
{ back: 'address1_name', front: 'address1' }, //Address
{ back: 'address1_city', front: 'city1' },
{ back: 'address1_postalcode', front: 'postalCode1' },
{ back: 'address2_name', front: 'address2' }, //Mailing Address
{ back: 'address2_city', front: 'city2' },
{ back: 'address2_postalcode', front: 'postalCode2' },
{ back: 'address1_primarycontactname', front: 'contactName' },
{ back: 'ccof_position', front: 'position' },
{ back: 'telephone1', front: 'phone' },
const UserProfileMappings = [
{ back: 'contactid', front: 'contactId' },
{ back: 'ccof_userid', front: 'userId' },
{ back: 'ccof_username', front: 'username' },
{ back: 'emailaddress1', front: 'email' },
{ back: 'ccof_instructionnumber', front: 'incNumber' },//incorporation number
{ back: 'ccof_typeoforganization', front: 'organizationType' },
{ back: 'ccof_formcomplete', front: 'isOrganizationComplete' },
{ back: 'ccof_is_mailing_address_same', front: 'isSameAsMailing'}
];
{ back: 'ofm_first_name', front: 'firstName' },
{ back: 'ofm_last_name', front: 'lastName' },
{ back: 'ofm_portal_role', front: 'roles' },
]

const ProgramYearMappings = [
{ back: 'ccof_program_yearid', front: 'programYearId' },
{ back: 'ccof_name', front: 'name' },
{ back: 'statuscode', front: 'status' },
{ back: 'ccof_programyearnumber', front: 'order' },
{ back: '_ccof_previousyear_value', front: 'previousYearId' },
{ back: 'ccof_intakeperiodstart', front: 'intakeStart' },
{ back: 'ccof_intakeperiodend', front: 'intakeEnd' },
{ back: 'ccof_declarationbstart', front: 'declarationbStart' },
];
const UserProfileOrganizationMappings = [
{ back: 'accountid', front: 'organizationId' },
{ back: 'accountnumber', front: 'organizationAccountNumber' },
{ back: 'ccof_accounttype', front: 'organizationAccountType' },
{ back: 'name', front: 'organizationName' },
{ back: 'statecode', front: 'organizationStateCode' },
{ back: 'statuscode', front: 'organizationStatus' },
]

const UserProfileFacilityPermissionMappings = [
{ back: 'statecode', front: 'stateCode' },
{ back: 'statuscode', front: 'statusCode' },
]

const UserProfileFacilityMappings = [
{ back: 'accountid', front: 'facilityId' },
{ back: 'accountnumber', front: 'facilityAccountNumber' },
{ back: 'name', front: 'facilityName' },
{ back: 'ccof_accounttype', front: 'facilityType' },
{ back: 'statecode', front: 'facilityStateCode' },
{ back: 'statuscode', front: 'facilityStatusCode' },
]

module.exports = {
OrganizationMappings,
ProgramYearMappings
};
UserProfileMappings,
UserProfileOrganizationMappings,
UserProfileFacilityPermissionMappings,
UserProfileFacilityMappings,
}
Loading
Loading