Skip to content

Commit

Permalink
Merge pull request #949 from NERC-CEH/ED-68-pre-create-users
Browse files Browse the repository at this point in the history
ED-68 Enable pre-creation of users
  • Loading branch information
iwalmsley authored Apr 15, 2024
2 parents 9c55547 + cd04f58 commit 8a9eda4
Show file tree
Hide file tree
Showing 24 changed files with 220 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ async function getUserRoles(req, res) {
const mappedUsers = userPermissions.map(user => ({
userId: user.userId,
role: find(user.projectRoles, { projectKey }).role,
verified: user.verified,
}));

res.send(mappedUsers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Array [
],
"userId": "uid1",
"userName": "user1",
"verified": true,
},
Object {
"catalogueRole": "publisher",
Expand All @@ -35,6 +36,7 @@ Array [
],
"userId": "uid2",
"userName": "user2",
"verified": true,
},
]
`;
Expand All @@ -57,6 +59,7 @@ Array [
],
"userId": "uid1",
"userName": "user1",
"verified": true,
},
Object {
"catalogueRole": "publisher",
Expand All @@ -70,6 +73,7 @@ Array [
],
"userId": "uid2",
"userName": "user2",
"verified": true,
},
Object {
"catalogueRole": "user",
Expand All @@ -83,6 +87,7 @@ Array [
],
"userId": "uid2",
"userName": "user2",
"verified": true,
},
Object {
"catalogueRole": "user",
Expand All @@ -103,6 +108,7 @@ exports[`userRolesRepository read operations getUser returns expected snapshot 1
Object {
"name": "user1",
"userId": "uid1",
"verified": true,
}
`;

Expand All @@ -111,10 +117,12 @@ Array [
Object {
"name": "user1",
"userId": "uid1",
"verified": true,
},
Object {
"name": "user2",
"userId": "uid2",
"verified": true,
},
]
`;
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ const wrapUser = user => ({
toObject: () => user,
});

// For specific findAndUpdate queries things are either set explictly on insert or updated
// flatten to ensure that the return is simply the record itself
function flattenUpdateEntity(entity) {
const flatObj = {};
Object.keys(entity).forEach((key) => {
if (key === '$set' || key === '$setOnInsert') {
Object.assign(flatObj, entity[key]);
} else {
flatObj[key] = entity[key];
}
});
return flatObj;
}

function createDatabaseMock(users) {
const documents = users.map(cloneUser).map(wrapUser);
let lastInvocation;
Expand All @@ -23,8 +37,9 @@ function createDatabaseMock(users) {
return { exec: () => Promise.resolve(findOneReturn) };
},
findOneAndUpdate: (query, entity, params) => {
const updatedEntity = wrapUser(cloneUser(flattenUpdateEntity(entity)));
lastInvocation = { query, entity, params };
return Promise.resolve(entity);
return Promise.resolve(updatedEntity);
},
remove: (query) => {
lastInvocation = { query };
Expand Down
62 changes: 46 additions & 16 deletions code/workspaces/auth-service/src/dataaccess/userRolesRepository.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,25 @@ function addDefaults(roles) {
async function getRoles(userId, userName) {
let roles = await UserRoles().findOne({ userId }).exec();
if (!roles) {
roles = await addRecordForNewUser(userId, userName);
// User has logged in and roles are being retrieved hence they are verified
roles = await createVerifiedUserRecord(userId, userName);
}

return addDefaults(roles);
}

async function createVerifiedUserRecord(userId, userName) {
return addRecordForNewUser(userId, userName, true);
}

async function createNonVerifiedUserRecord(userId, userName) {
return addRecordForNewUser(userId, userName, false);
}

function convertToUser(roles) {
const userRoles = roles.toObject();
const userName = userRoles.userName ? userRoles.userName : 'Unknown user name';
const user = { userId: userRoles.userId, name: userName };
const user = { userId: userRoles.userId, name: userName, verified: userRoles.verified };
return user;
}

Expand Down Expand Up @@ -94,23 +103,40 @@ async function getProjectUsers(projectKey) {
return projectUsers.map(addDefaults);
}

async function addRecordForNewUser(userId, userName) {
async function addRecordForNewUser(userId, userName, verified = true) {
const allRoles = await UserRoles().find().exec();
if (allRoles.filter(roles => roles.userId === userId).length > 0) {
throw new Error(`Creating new user for ${userId} ${userName}, but they already exist`);
if (doesUserIdExist(allRoles, userId)) {
throw new Error(`Creating new user for userId: ${userId} ${userName}, but they already exist`);
}
const user = {

// Create user - explictly overriding only userId & verified fields
const overrideFields = {
userId,
userName,
projectRoles: [],
verified,
};

if (allRoles.length === 0) {
// Make the first ever user an instanceAdmin.
logger.info(`${userName} is first user, so making instanceAdmin`);
user.instanceAdmin = true;
overrideFields.instanceAdmin = true;
}
const roles = await UserRoles().create(user);
return roles;

// findAndUpdate used over create as user record may legitimately either
// - Not exist
// - Exist with set permissions prior to login
// Hence upsert based on userName overriding userId explictly.
return UserRoles().findOneAndUpdate(
{ userName },
{
$set: overrideFields,
$setOnInsert: { projectRoles: [] },
},
{ new: true, upsert: true },
);
}

function doesUserIdExist(allUsers, userId) {
return allUsers.filter(roles => roles.userId === userId).length > 0;
}

// Note - roleKey must exist in UserRolesSchema in userRoles.model.js
Expand All @@ -130,10 +156,10 @@ async function setSystemRole(userId, roleKey, roleValue) {
async function addRole(userId, projectKey, role) {
// Load existing user
const query = { userId };
const user = await UserRoles().findOne(query).exec();

let user = await UserRoles().findOne(query).exec();
if (!user) {
throw new Error(`Unrecognised user ${userId}`);
// If a user record does not exist, generate it with verified field set to false
user = await createNonVerifiedUserRecord(userId, userId);
}
// Either add role or update existing role
const { projectRoles } = user;
Expand Down Expand Up @@ -170,8 +196,11 @@ async function userIsMember(userId, projectKey) {
return UserRoles().exists(query);
}

export default { combineRoles,
export default {
combineRoles,
getRoles,
createNonVerifiedUserRecord,
createVerifiedUserRecord,
addRecordForNewUser,
getUser,
getUsers,
Expand All @@ -180,4 +209,5 @@ export default { combineRoles,
setSystemRole,
addRole,
removeRole,
userIsMember };
userIsMember,
};
Loading

0 comments on commit 8a9eda4

Please sign in to comment.