chore: Refactor user activation to command

This commit is contained in:
Tom Moor
2022-08-18 11:24:27 +02:00
parent f620a9d34c
commit f32f07cdcc
5 changed files with 105 additions and 17 deletions

View File

@@ -10,6 +10,10 @@ type Props = {
ip: string; ip: string;
}; };
/**
* This command suspends an active user, this will cause them to lose access to
* the team.
*/
export default async function userSuspender({ export default async function userSuspender({
user, user,
actorId, actorId,

View File

@@ -0,0 +1,46 @@
import { buildAdmin, buildUser } from "@server/test/factories";
import { getTestDatabase } from "@server/test/support";
import userUnsuspender from "./userUnsuspender";
const db = getTestDatabase();
afterAll(db.disconnect);
beforeEach(db.flush);
describe("userUnsuspender", () => {
const ip = "127.0.0.1";
it("should not allow unsuspending self", async () => {
const user = await buildUser();
let error;
try {
await userUnsuspender({
actorId: user.id,
user,
ip,
});
} catch (err) {
error = err;
}
expect(error.message).toEqual("Unable to unsuspend the current user");
});
it("should unsuspend the user", async () => {
const admin = await buildAdmin();
const user = await buildUser({
teamId: admin.teamId,
suspendedAt: new Date(),
suspendedById: admin.id,
});
await userUnsuspender({
actorId: admin.id,
user,
ip,
});
expect(user.suspendedAt).toEqual(null);
expect(user.suspendedById).toEqual(null);
});
});

View File

@@ -0,0 +1,49 @@
import { Transaction } from "sequelize";
import { sequelize } from "@server/database/sequelize";
import { User, Event } from "@server/models";
import { ValidationError } from "../errors";
type Props = {
user: User;
actorId: string;
ip: string;
};
/**
* This command unsuspends a previously suspended user, allowing access to the
* team again.
*/
export default async function userUnsuspender({
user,
actorId,
ip,
}: Props): Promise<void> {
if (user.id === actorId) {
throw ValidationError("Unable to unsuspend the current user");
}
await sequelize.transaction(async (transaction: Transaction) => {
await user.update(
{
suspendedById: null,
suspendedAt: null,
},
{ transaction }
);
await Event.create(
{
name: "users.activate",
actorId,
userId: user.id,
teamId: user.teamId,
data: {
name: user.name,
},
ip,
},
{
transaction,
}
);
});
}

View File

@@ -438,13 +438,6 @@ class User extends ParanoidModel {
}); });
}; };
activate = () => {
return this.update({
suspendedById: null,
suspendedAt: null,
});
};
// hooks // hooks
@BeforeDestroy @BeforeDestroy

View File

@@ -7,6 +7,7 @@ import userDemoter from "@server/commands/userDemoter";
import userDestroyer from "@server/commands/userDestroyer"; import userDestroyer from "@server/commands/userDestroyer";
import userInviter from "@server/commands/userInviter"; import userInviter from "@server/commands/userInviter";
import userSuspender from "@server/commands/userSuspender"; import userSuspender from "@server/commands/userSuspender";
import userUnsuspender from "@server/commands/userUnsuspender";
import { sequelize } from "@server/database/sequelize"; import { sequelize } from "@server/database/sequelize";
import ConfirmUserDeleteEmail from "@server/emails/templates/ConfirmUserDeleteEmail"; import ConfirmUserDeleteEmail from "@server/emails/templates/ConfirmUserDeleteEmail";
import InviteEmail from "@server/emails/templates/InviteEmail"; import InviteEmail from "@server/emails/templates/InviteEmail";
@@ -284,21 +285,16 @@ router.post("users.suspend", auth(), async (ctx) => {
router.post("users.activate", auth(), async (ctx) => { router.post("users.activate", auth(), async (ctx) => {
const userId = ctx.body.id; const userId = ctx.body.id;
const teamId = ctx.state.user.teamId;
const actor = ctx.state.user; const actor = ctx.state.user;
assertPresent(userId, "id is required"); assertPresent(userId, "id is required");
const user = await User.findByPk(userId); const user = await User.findByPk(userId, {
rejectOnEmpty: true,
});
authorize(actor, "activate", user); authorize(actor, "activate", user);
await user.activate(); await userUnsuspender({
await Event.create({ user,
name: "users.activate",
actorId: actor.id, actorId: actor.id,
userId,
teamId,
data: {
name: user.name,
},
ip: ctx.request.ip, ip: ctx.request.ip,
}); });
const includeDetails = can(actor, "readDetails", user); const includeDetails = can(actor, "readDetails", user);