Skip to content

Commit

Permalink
Merge pull request #3501 from uselagoon/password-resets
Browse files Browse the repository at this point in the history
feat: support password resets from the api
  • Loading branch information
tobybellwood authored Aug 3, 2023
2 parents f111315 + 5b10b05 commit 7b5990b
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 4 deletions.
1 change: 1 addition & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ services:
- ./node-packages:/app/node-packages:delegated
- /app/node-packages/commons/dist
environment:
- KEYCLOAK_URL=http://172.17.0.1:8088
- NODE_ENV=development
- OPENSEARCH_INTEGRATION_ENABLED=false
- DISABLE_CORE_HARBOR=true
Expand Down
37 changes: 34 additions & 3 deletions services/api/src/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Group, isRoleSubgroup } from './group';
import { sqlClientPool } from '../clients/sqlClient';
import { query } from '../util/db';
import { Sql } from '../resources/user/sql';
import { getConfigFromEnv } from '../util/config';

interface IUserAttributes {
comment?: [string];
Expand Down Expand Up @@ -49,9 +50,10 @@ interface UserModel {
projectId: number,
userGroups: Group[]
) => Promise<string[]>;
addUser: (userInput: User) => Promise<User>;
addUser: (userInput: User, resetPassword?: Boolean) => Promise<User>;
updateUser: (userInput: UserEdit) => Promise<User>;
deleteUser: (id: string) => Promise<void>;
resetUserPassword: (id: string) => Promise<void>;
}

interface AttributeFilterFn {
Expand Down Expand Up @@ -472,7 +474,7 @@ export const User = (clients: {
return R.uniq(roles);
};

const addUser = async (userInput: User): Promise<User> => {
const addUser = async (userInput: User, resetPassword?: Boolean): Promise<User> => {
let response: { id: string };
try {
response = await keycloakAdminClient.users.create({
Expand Down Expand Up @@ -502,6 +504,16 @@ export const User = (clients: {
await linkUserToGitlab(user, R.prop('gitlabId', userInput));
}

if (resetPassword) {
await keycloakAdminClient.users.executeActionsEmail({
id: user.id,
lifespan: 43200,
actions: ["UPDATE_PASSWORD"],
clientId: "lagoon-ui",
redirectUri: getConfigFromEnv('LAGOON_UI', "http://localhost:8888")
});
}

return {
...user,
gitlabId: R.prop('gitlabId', userInput)
Expand Down Expand Up @@ -534,6 +546,24 @@ export const User = (clients: {
)(user);
}

const resetUserPassword = async (id: string): Promise<void> => {
try {
await keycloakAdminClient.users.executeActionsEmail({
id: id,
lifespan: 43200,
actions: ["UPDATE_PASSWORD"],
clientId: "lagoon-ui",
redirectUri: getConfigFromEnv('LAGOON_UI', "http://localhost:8888")
});
} catch (err) {
if (err.response.status && err.response.status === 404) {
throw new UserNotFoundError(`User not found: ${id}`);
} else {
throw new Error(`Error updating Keycloak user: ${err.message}`);
}
}
};

const updateUser = async (userInput: UserEdit): Promise<User> => {
// comments used to be removed when updating a user, now they aren't
let organizations = null;
Expand Down Expand Up @@ -661,6 +691,7 @@ export const User = (clients: {
getUserRolesForProject,
addUser,
updateUser,
deleteUser
deleteUser,
resetUserPassword
};
};
2 changes: 2 additions & 0 deletions services/api/src/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ const {
updateUser,
addUserToOrganization,
removeUserFromOrganization,
resetUserPassword,
deleteUser,
deleteAllUsers,
getAllUsers,
Expand Down Expand Up @@ -627,6 +628,7 @@ const resolvers = {
updateUser,
addUserToOrganization,
removeUserFromOrganization,
resetUserPassword,
deleteUser,
deleteAllUsers,
addDeployment,
Expand Down
22 changes: 21 additions & 1 deletion services/api/src/resources/user/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export const addUser: ResolverFn = async (
lastName: input.lastName,
comment: input.comment,
gitlabId: input.gitlabId,
});
}, input.resetPassword);

return user;
};
Expand Down Expand Up @@ -171,6 +171,26 @@ export const updateUser: ResolverFn = async (
return updatedUser;
};

export const resetUserPassword: ResolverFn = async (
_root,
{ input: { user: userInput } },
{ models, hasPermission },
) => {
const user = await models.UserModel.loadUserByIdOrUsername({
id: R.prop('id', userInput),
username: R.prop('email', userInput),
});

// someone can reset their own password if they want to, but admins will be able to do this
await hasPermission('user', 'update', {
users: [user.id],
});

await models.UserModel.resetUserPassword(user.id);

return 'success';
};

export const deleteUser: ResolverFn = async (
_root,
{ input: { user: userInput } },
Expand Down
6 changes: 6 additions & 0 deletions services/api/src/typeDefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -1803,6 +1803,7 @@ const typeDefs = gql`
lastName: String
comment: String
gitlabId: Int
resetPassword: Boolean
}
input UpdateUserPatchInput {
Expand All @@ -1828,6 +1829,10 @@ const typeDefs = gql`
owner: Boolean
}
input ResetUserPasswordInput {
user: UserInput!
}
input DeleteProjectInput {
project: String!
}
Expand Down Expand Up @@ -2263,6 +2268,7 @@ const typeDefs = gql`
"""
addUserToOrganization(input: addUserToOrganizationInput!): User
removeUserFromOrganization(input: addUserToOrganizationInput!): User
resetUserPassword(input: ResetUserPasswordInput!): String
deleteUser(input: DeleteUserInput!): String
deleteAllUsers: String
addDeployment(input: AddDeploymentInput!): Deployment
Expand Down

0 comments on commit 7b5990b

Please sign in to comment.