| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- import fs from 'node:fs'
- import minimist from 'minimist'
- import InstitutionsAPIModule from '../../app/src/Features/Institutions/InstitutionsAPI.js'
- const { promises: InstitutionsAPI } = InstitutionsAPIModule
- const argv = minimist(process.argv.slice(2))
- const commit = argv.commit !== undefined
- const ignoreNulls = !!argv['ignore-nulls']
- if (!commit) {
- console.log('DOING DRY RUN. TO SAVE CHANGES PASS --commit')
- }
- const userEntitlements = loadUserEntitlements(argv['user-entitlements'])
- const cachedEntitlements = loadCachedEntitlements(argv['cached-entitlements'])
- async function syncUserEntitlements(userEntitlements, cachedEntitlements) {
- // check for user entitlements in mongo but not in postgres
- for (const key of Object.keys(userEntitlements)) {
- const userEntitlement = userEntitlements[key]
- if (!userEntitlement) {
- continue
- }
- // find any email(s) that are linked through sso
- for (const email of userEntitlement.emails) {
- if (!email.samlProviderId) {
- continue
- }
- // get samlIdentifiers entry for email
- const samlIdentifier = userEntitlement.samlIdentifiers.find(
- samlIdentifier => samlIdentifier.providerId === email.samlProviderId
- )
- // validate that entitlement is cached
- if (samlIdentifier) {
- const cachedEntitlment = cachedEntitlements[email.email]
- // validate that record is correct
- if (cachedEntitlment) {
- if (
- cachedEntitlment.hasEntitlement !== samlIdentifier.hasEntitlement
- ) {
- console.log(
- `cached entitlement mismatch for user ${userEntitlement.userId} mongo(${samlIdentifier.hasEntitlement}) postgres(${cachedEntitlment.hasEntitlement})`
- )
- await syncUserEntitlement(
- userEntitlement.userId,
- email.email,
- samlIdentifier.hasEntitlement
- )
- }
- }
- // there is not record in postgres at all
- else {
- console.log(
- `missing cached entitlement for user ${userEntitlement.userId}`
- )
- await syncUserEntitlement(
- userEntitlement.userId,
- email.email,
- samlIdentifier.hasEntitlement
- )
- }
- }
- // if identifier is missing for email this is internal inconsistency in mongo
- else {
- console.log(`missing samlIdentifier for user ${userEntitlement.userId}`)
- }
- }
- // find any samlIdentifier records missing email entry
- for (const samlIdentifier of userEntitlement.samlIdentifiers) {
- const email = userEntitlement.emails.find(
- email => email.samlProviderId === samlIdentifier.providerId
- )
- if (!email) {
- console.log(
- `missing email entry for samlIdentifier for user ${userEntitlement.userId}`
- )
- }
- }
- }
- // check for user entitlements in postgres but not in mongo
- for (const key of Object.keys(cachedEntitlements)) {
- const cachedEntitlment = cachedEntitlements[key]
- if (!cachedEntitlment) {
- continue
- }
- if (!cachedEntitlment.hasEntitlement) {
- continue
- }
- const userEntitlement = userEntitlements[cachedEntitlment.userId]
- // validate that mongo has correct entitlement
- if (userEntitlement) {
- // find samlIdentifier for provider
- const samlIdentifier = userEntitlement.samlIdentifiers.find(
- samlIdentifier =>
- samlIdentifier.providerId === cachedEntitlment.providerId
- )
- if (!samlIdentifier || !samlIdentifier.hasEntitlement) {
- console.log(
- `cached entitlement mismatch for user ${userEntitlement.userId} mongo(false) postgres(true)`
- )
- await syncUserEntitlement(
- userEntitlement.userId,
- cachedEntitlment.email,
- false
- )
- }
- }
- // if the record does not exist it is probably because users without
- // entitlements were not exported
- else {
- console.log(
- `missing cached entitlement in mongo for user ${cachedEntitlment.userId}`
- )
- }
- }
- }
- async function syncUserEntitlement(userId, email, hasEntitlement) {
- if (!commit) {
- return
- }
- try {
- if (hasEntitlement) {
- await InstitutionsAPI.addEntitlement(userId, email)
- } else {
- await InstitutionsAPI.removeEntitlement(userId, email)
- }
- } catch (err) {
- console.error(
- `error setting entitlement: ${userId}, ${email}, ${hasEntitlement} - ${err.message}`
- )
- }
- }
- function loadUserEntitlements(userEntitlementsFilename) {
- const userEntitlementsData = fs
- .readFileSync(userEntitlementsFilename, {
- encoding: 'utf8',
- })
- .split('\n')
- const userEntitlements = {}
- for (const userEntitlementLine of userEntitlementsData) {
- if (!userEntitlementLine) {
- continue
- }
- const userEntitlementExport = JSON.parse(userEntitlementLine)
- const userId = userEntitlementExport._id.$oid
- delete userEntitlementExport._id
- userEntitlementExport.userId = userId
- userEntitlements[userId] = userEntitlementExport
- }
- return userEntitlements
- }
- function loadCachedEntitlements(cachedEntitlementsFilename) {
- const cachedEntitlementsData = fs
- .readFileSync(cachedEntitlementsFilename, {
- encoding: 'utf8',
- })
- .split('\n')
- const cachedEntitlements = {}
- for (const cachedEntitlementLine of cachedEntitlementsData) {
- // this is safe because comma is not an allowed value for any column
- const [userId, email, hasEntitlement, providerId] =
- cachedEntitlementLine.split(',')
- let hasEntitlementBoolean
- if (ignoreNulls) {
- hasEntitlementBoolean = hasEntitlement === 't'
- } else {
- hasEntitlementBoolean =
- hasEntitlement === 't' ? true : hasEntitlement === 'f' ? false : null
- }
- cachedEntitlements[email] = {
- email,
- hasEntitlement: hasEntitlementBoolean,
- providerId,
- userId,
- }
- }
- return cachedEntitlements
- }
- try {
- await syncUserEntitlements(userEntitlements, cachedEntitlements)
- } catch (error) {
- console.error(error.stack)
- }
- process.exit()
|