✨New endpoint that is marking user for deletion
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
<env name="PUBSUB_TYPE" value="local" />
|
<env name="PUBSUB_TYPE" value="local" />
|
||||||
<env name="SEARCH_DRIVER" value="mongodb" />
|
<env name="SEARCH_DRIVER" value="mongodb" />
|
||||||
<env name="STORAGE_LOCAL_PATH" value="/tmp" />
|
<env name="STORAGE_LOCAL_PATH" value="/tmp" />
|
||||||
|
<env name="ADMIN_ENDPOINT_SECRET" value="123" />
|
||||||
</envs>
|
</envs>
|
||||||
<scope-kind value="DIRECTORY" />
|
<scope-kind value="DIRECTORY" />
|
||||||
<test-directory value="$PROJECT_DIR$/tdrive/backend/node/test/e2e" />
|
<test-directory value="$PROJECT_DIR$/tdrive/backend/node/test/e2e" />
|
||||||
|
|||||||
+19
-1
@@ -1,4 +1,3 @@
|
|||||||
import { UserPrimaryKey } from "src/services/user/entities/user";
|
|
||||||
import gr from "../../../../../services/global-resolver";
|
import gr from "../../../../../services/global-resolver";
|
||||||
|
|
||||||
import type { ExecutionContext } from "../../../../platform/framework/api/crud-service";
|
import type { ExecutionContext } from "../../../../platform/framework/api/crud-service";
|
||||||
@@ -13,6 +12,25 @@ export class AdminDeleteUserController {
|
|||||||
return this._repos;
|
return this._repos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async markToDelete(
|
||||||
|
userId: string,
|
||||||
|
): Promise<{ status: "failed" | "deleting" | "done"; userId?: string }> {
|
||||||
|
try {
|
||||||
|
await gr.services.console.getClient().userWasDeletedForceLogout(userId);
|
||||||
|
await gr.services.users.markToDeleted({ id: userId });
|
||||||
|
} catch (err) {
|
||||||
|
adminLogger.error({ err, userId }, "[DELETE USER] Error dustin updating user ");
|
||||||
|
return {
|
||||||
|
status: "failed",
|
||||||
|
userId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: "done",
|
||||||
|
userId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/** Begin or forward the deletion process of a user, if `deleteData` is false, only anonymises the user entry */
|
/** Begin or forward the deletion process of a user, if `deleteData` is false, only anonymises the user entry */
|
||||||
async deleteUser(
|
async deleteUser(
|
||||||
userId: string,
|
userId: string,
|
||||||
|
|||||||
@@ -62,6 +62,19 @@ const routes: FastifyPluginCallback = async (fastify: FastifyInstance, _opts, ne
|
|||||||
|
|
||||||
return { id: userId };
|
return { id: userId };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fastify.post(`${urlRoot}/mark`, async (request, reply) => {
|
||||||
|
if (!authenticateAdminQuery(request, reply)) return false;
|
||||||
|
|
||||||
|
const { email } = request.body as TUserGetIdByEmailBody;
|
||||||
|
const userId = await controller.getUserIdByEmail(email);
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
return reply.status(404).send({ message: "User not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return await controller.markToDelete(userId);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -75,6 +75,10 @@ export default class User {
|
|||||||
@Column("password", "string")
|
@Column("password", "string")
|
||||||
password: string;
|
password: string;
|
||||||
|
|
||||||
|
//just mark user as deleted not to be able to login
|
||||||
|
@Column("marked_to_delete", "tdrive_boolean")
|
||||||
|
marked_to_delete: boolean;
|
||||||
|
|
||||||
@Column("deleted", "tdrive_boolean")
|
@Column("deleted", "tdrive_boolean")
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
|
|
||||||
|
|||||||
@@ -201,6 +201,12 @@ export class UserServiceImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async markToDeleted(pk: UserPrimaryKey) {
|
||||||
|
const user = await this.get(pk);
|
||||||
|
user.marked_to_delete = true;
|
||||||
|
await this.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
async search(
|
async search(
|
||||||
pagination: Pagination,
|
pagination: Pagination,
|
||||||
options?: SearchUserOptions,
|
options?: SearchUserOptions,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export const userObjectSchema = {
|
|||||||
last_name: { type: "string" },
|
last_name: { type: "string" },
|
||||||
created_at: { type: "number" },
|
created_at: { type: "number" },
|
||||||
deleted: { type: "boolean" },
|
deleted: { type: "boolean" },
|
||||||
|
marked_to_delete: { type: "boolean" },
|
||||||
delete_process_started_epoch: { type: "number" },
|
delete_process_started_epoch: { type: "number" },
|
||||||
|
|
||||||
status: { type: "string" },
|
status: { type: "string" },
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ export interface UserObject {
|
|||||||
full_name: string;
|
full_name: string;
|
||||||
created_at: number;
|
created_at: number;
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
|
marked_to_delete: boolean;
|
||||||
status: string; //Single string for the status
|
status: string; //Single string for the status
|
||||||
last_activity: number;
|
last_activity: number;
|
||||||
last_seen?: number;
|
last_seen?: number;
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ export class WorkspaceUsersCrudController
|
|||||||
full_name: [user.first_name, user.last_name].join(" "),
|
full_name: [user.first_name, user.last_name].join(" "),
|
||||||
created_at: user.creation_date,
|
created_at: user.creation_date,
|
||||||
deleted: Boolean(user.deleted),
|
deleted: Boolean(user.deleted),
|
||||||
|
marked_to_delete: Boolean(user.marked_to_delete),
|
||||||
status: user.status_icon,
|
status: user.status_icon,
|
||||||
last_activity: user.last_activity,
|
last_activity: user.last_activity,
|
||||||
cache: {
|
cache: {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export async function formatUser(
|
|||||||
full_name: [user.first_name, user.last_name].join(" "),
|
full_name: [user.first_name, user.last_name].join(" "),
|
||||||
created_at: user.creation_date,
|
created_at: user.creation_date,
|
||||||
deleted: Boolean(user.deleted),
|
deleted: Boolean(user.deleted),
|
||||||
|
marked_to_delete: Boolean(user.marked_to_delete),
|
||||||
delete_process_started_epoch: user.delete_process_started_epoch,
|
delete_process_started_epoch: user.delete_process_started_epoch,
|
||||||
status: user.status_icon,
|
status: user.status_icon,
|
||||||
last_activity: user.last_activity,
|
last_activity: user.last_activity,
|
||||||
|
|||||||
@@ -30,9 +30,11 @@ type TokenPayload = {
|
|||||||
|
|
||||||
export type User = {
|
export type User = {
|
||||||
id: string;
|
id: string;
|
||||||
|
email?: string;
|
||||||
first_name?: string;
|
first_name?: string;
|
||||||
isWorkspaceModerator?: boolean;
|
isWorkspaceModerator?: boolean;
|
||||||
deleted?: boolean,
|
deleted?: boolean,
|
||||||
|
marked_to_delete?: boolean;
|
||||||
delete_process_started_epoch?: number,
|
delete_process_started_epoch?: number,
|
||||||
preferences?: {
|
preferences?: {
|
||||||
language?: string;
|
language?: string;
|
||||||
|
|||||||
@@ -301,10 +301,32 @@ describe("The users deletion API", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should have no objects left other than user (and untestable files)", async () => {
|
it("should have no objects left other than user (and untestable files)", async () => {
|
||||||
expect(await (await Promise.all(
|
expect((await Promise.all(
|
||||||
Object.entries(listByUserIn)
|
Object.entries(listByUserIn)
|
||||||
.flatMap(async ([key, fn]) => (await fn(myUserId)).getEntities().map(e => ({"": e.constructor.name, ...e})))
|
.flatMap(async ([_, fn]) => (await fn(myUserId)).getEntities().map(e => ({"": e.constructor.name, ...e})))
|
||||||
)).flat()).toHaveLength(0);
|
)).flat()).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("'existing user with to_delete flag' should return 404 if user is not found", async () => {
|
||||||
|
const secret = adminConfig.endpointSecret;
|
||||||
|
const email = "notexistingemail@test.com";
|
||||||
|
const response = await platform!.app.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: `/admin/api/user/delete/mark`,
|
||||||
|
body: { secret, email },
|
||||||
|
});
|
||||||
|
expect(response.statusCode).toBe(404);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("'existing user with to_delete flag' should return 200", async () => {
|
||||||
|
const user = await UserApi.getInstance(platform);
|
||||||
|
const response = await platform!.app.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: `/admin/api/user/delete/mark`,
|
||||||
|
body: { secret: adminConfig.endpointSecret, email: (await user.getUser()).email },
|
||||||
|
});
|
||||||
|
expect(response.statusCode).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user