diff --git a/admin_api_stack/lib/admin_lambda.ts b/admin_api_stack/lib/admin_lambda.ts
index 88189e3..a6ea11e 100644
--- a/admin_api_stack/lib/admin_lambda.ts
+++ b/admin_api_stack/lib/admin_lambda.ts
@@ -149,6 +149,7 @@ export class LambdaService extends Construct {
"cognito-idp:AdminEnableUser",
"cognito-idp:AdminDisableUser",
"cognito-idp:AdminRemoveUserFromGroup",
+ "cognito-idp:AdminResetUserPassword",
"cognito-idp:AdminAddUserToGroup",
"cognito-idp:AdminListGroupsForUser",
"cognito-idp:AdminGetUser",
diff --git a/admin_api_stack/user_list_resources/app.js b/admin_api_stack/user_list_resources/app.js
index cae2e4f..73c0d51 100644
--- a/admin_api_stack/user_list_resources/app.js
+++ b/admin_api_stack/user_list_resources/app.js
@@ -20,6 +20,7 @@ const {
addUserToGroup,
removeUser,
removeUserFromGroup,
+ resetUserPassword,
confirmUserSignUp,
disableUser,
enableUser,
@@ -98,7 +99,7 @@ app.post('/addUser', async (req, res, next) => {
try {
// fetch the authorized user for contacting the JACS API.
const {email: authUser} = req.apiGateway.event.requestContext.authorizer.claims;
- const response = await addUser(req.body.username, authUser);
+ const response = await addUser(req.body.username, authUser, req.body.resend);
res.status(200).json(response);
} catch (err) {
next(err);
@@ -136,6 +137,21 @@ app.post('/removeUser', async (req, res, next) => {
}
});
+app.post('/resetUserPassword', async (req, res, next) => {
+ if (!req.body.username ) {
+ const err = new Error('username is required');
+ err.statusCode = 400;
+ return next(err);
+ }
+
+ try {
+ const response = await resetUserPassword(req.body.username);
+ res.status(200).json(response);
+ } catch (err) {
+ next(err);
+ }
+});
+
app.post('/confirmUserSignUp', async (req, res, next) => {
if (!req.body.username) {
const err = new Error('username is required');
diff --git a/admin_api_stack/user_list_resources/cognitoActions.js b/admin_api_stack/user_list_resources/cognitoActions.js
index 24ed3f5..86f1f14 100644
--- a/admin_api_stack/user_list_resources/cognitoActions.js
+++ b/admin_api_stack/user_list_resources/cognitoActions.js
@@ -85,7 +85,8 @@ async function getAuthToken(username) {
return token;
}
-async function addUser(username, authUser) {
+async function addUser(username, authUser, resend) {
+ console.log({resend});
console.log(`Attempting to add ${username} to userpool ${userPoolId}`);
const passwordParams = {
@@ -115,30 +116,38 @@ async function addUser(username, authUser) {
}
]
};
+
+ if (resend) {
+ params.MessageAction = 'RESEND';
+ delete params.UserAttributes;
+ }
+
await cognitoIdentityServiceProvider
.adminCreateUser(params)
.promise();
console.log(`Success adding ${username} to userpool ${userPoolId}`);
// code to connect to Workstation API and add the user.
- const authToken = await getAuthToken(authUser);
- await sendRequest(
- "/SCSW/JACS2SyncServices/v2/data/user",
- "PUT",
- {
- key: `user:${username}`,
- name: username,
- fullName: username,
- email: username,
- password: '',
- class: "org.janelia.model.security.User"
- },
- authToken
- );
+ if (!resend) {
+ const authToken = await getAuthToken(authUser);
+ await sendRequest(
+ "/SCSW/JACS2SyncServices/v2/data/user",
+ "PUT",
+ {
+ key: `user:${username}`,
+ name: username,
+ fullName: username,
+ email: username,
+ password: '',
+ class: "org.janelia.model.security.User"
+ },
+ authToken
+ );
- return {
- message: `Success adding ${username} to userpool`
- };
+ return {
+ message: `Success adding ${username} to userpool`
+ };
+ }
} catch (err) {
console.log(err);
throw err;
@@ -199,6 +208,27 @@ async function addUserToGroup(username, groupname, authUser) {
}
}
+async function resetUserPassword(username) {
+ const params = {
+ UserPoolId: userPoolId,
+ Username: username
+ };
+
+ console.log(`Attempting to reset password for ${username}`);
+ try {
+ await cognitoIdentityServiceProvider
+ .adminResetUserPassword(params)
+ .promise();
+ console.log(`Reset password for ${username} in userpool ${userPoolId}`);
+ return {
+ message: `Reset password for ${username}`
+ };
+ } catch (err) {
+ console.log(err);
+ throw err;
+ }
+}
+
async function removeUser(username) {
const params = {
UserPoolId: userPoolId,
@@ -482,6 +512,7 @@ module.exports = {
addUser,
addUserToGroup,
removeUser,
+ resetUserPassword,
removeUserFromGroup,
confirmUserSignUp,
disableUser,
diff --git a/vpc_stack/src/jacs/install-jacs-stack.sh b/vpc_stack/src/jacs/install-jacs-stack.sh
index 3fb0260..9dbc9fb 100644
--- a/vpc_stack/src/jacs/install-jacs-stack.sh
+++ b/vpc_stack/src/jacs/install-jacs-stack.sh
@@ -259,7 +259,7 @@ function createScheduledJobs() {
echo "Create scheduled jobs from $(cat ${DEPLOY_DIR}/local/scheduled_jobs.json)"
./manage.sh mongo \
-tool mongoimport \
- -run-opts "-v ${DEPLOY_DIR}/local:/local" \
+ -notty -run-opts "-v ${DEPLOY_DIR}/local:/local" \
--collection jacsScheduledService /local/scheduled_jobs.json
}
diff --git a/website/src/components/AdminDataTable.jsx b/website/src/components/AdminDataTable.jsx
index 2bd765c..34e7ec5 100644
--- a/website/src/components/AdminDataTable.jsx
+++ b/website/src/components/AdminDataTable.jsx
@@ -4,6 +4,7 @@ import PropTypes from "prop-types";
import AdminStatus from "./AdminStatus";
import ActiveStatus from "./ActiveStatus";
import DeleteUser from "./DeleteUser";
+import ResetUser from "./ResetUser";
export default function AdminDataTable({ loading, dataSource }) {
const columns = [
@@ -35,7 +36,12 @@ export default function AdminDataTable({ loading, dataSource }) {
title: "Action",
key: "action",
render: (text, record) => {
- return ;
+ return (
+ <>
+
+
+ >
+ );
},
},
];
diff --git a/website/src/components/ResetUser.jsx b/website/src/components/ResetUser.jsx
new file mode 100644
index 0000000..97ce827
--- /dev/null
+++ b/website/src/components/ResetUser.jsx
@@ -0,0 +1,57 @@
+import { Button, Popconfirm, message } from "antd";
+import { QuestionCircleOutlined } from "@ant-design/icons";
+import { PropTypes } from "prop-types";
+import { API } from "aws-amplify";
+import { useQueryClient } from "react-query";
+
+export default function ResetUser({ username, status }) {
+ const queryClient = useQueryClient();
+
+ function resetUser() {
+ API.post("AppStreamAPI", "resetUserPassword", {
+ body: { username },
+ })
+ .then(() => {
+ queryClient.invalidateQueries("users");
+ message.success(`resetting password for user: ${username}`);
+ })
+ .catch((e) => {
+ const responseMessage = e?.response?.data?.message || e.message;
+ message.error(responseMessage);
+ });
+ }
+
+ function resendInvite() {
+ API.post("AppStreamAPI", "/addUser", {
+ body: { username, resend: 1 },
+ })
+ .then(() => {
+ queryClient.invalidateQueries("users");
+ message.success(`resending invite for user: ${username}`);
+ })
+ .catch((e) => {
+ const responseMessage = e?.response?.data?.message || e.message;
+ message.error(responseMessage);
+ });
+ }
+
+ if (status === "FORCE_CHANGE_PASSWORD") {
+ return (
+ }
+ >
+
+
+ );
+ }
+ return null;
+}
+
+ResetUser.propTypes = {
+ username: PropTypes.string.isRequired,
+ status: PropTypes.string.isRequired,
+};