mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Merge pull request #634 from directus/delete-last-admin
delete last admin
This commit is contained in:
@@ -9,3 +9,4 @@ export * from './invalid-payload';
|
||||
export * from './invalid-query';
|
||||
export * from './route-not-found';
|
||||
export * from './service-unavailable';
|
||||
export * from './unprocessable-entity';
|
||||
|
||||
7
api/src/exceptions/unprocessable-entity.ts
Normal file
7
api/src/exceptions/unprocessable-entity.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { BaseException } from './base';
|
||||
|
||||
export class UnprocessableEntityException extends BaseException {
|
||||
constructor(message: string) {
|
||||
super(message, 422, 'UNPROCESSABLE_ENTITY');
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { AbstractServiceOptions, PrimaryKey } from '../types';
|
||||
import { PermissionsService } from './permissions';
|
||||
import { UsersService } from './users';
|
||||
import { PresetsService } from './presets';
|
||||
import { UnprocessableEntityException } from '../exceptions';
|
||||
|
||||
export class RolesService extends ItemsService {
|
||||
constructor(options?: AbstractServiceOptions) {
|
||||
@@ -14,15 +15,28 @@ export class RolesService extends ItemsService {
|
||||
async delete(key: PrimaryKey | PrimaryKey[]): Promise<PrimaryKey | PrimaryKey[]> {
|
||||
const keys = Array.isArray(key) ? key : [key];
|
||||
|
||||
// Make sure there's at least one admin role left after this deletion is done
|
||||
const otherAdminRoles = await this.knex
|
||||
.count('*', { as: 'count' })
|
||||
.from('directus_roles')
|
||||
.whereNotIn('id', keys)
|
||||
.andWhere({ admin_access: true })
|
||||
.first();
|
||||
const otherAdminRolesCount = +(otherAdminRoles?.count || 0);
|
||||
if (otherAdminRolesCount === 0)
|
||||
throw new UnprocessableEntityException(`You can't delete the last admin role.`);
|
||||
|
||||
// Remove all permissions associated with this role
|
||||
const permissionsService = new PermissionsService({
|
||||
knex: this.knex,
|
||||
accountability: this.accountability,
|
||||
});
|
||||
const permissionsForRole = await permissionsService.readByQuery({
|
||||
|
||||
const permissionsForRole = (await permissionsService.readByQuery({
|
||||
fields: ['id'],
|
||||
filter: { role: { _in: keys } },
|
||||
}) as { id: number }[];
|
||||
})) as { id: number }[];
|
||||
|
||||
const permissionIDs = permissionsForRole.map((permission) => permission.id);
|
||||
await permissionsService.delete(permissionIDs);
|
||||
|
||||
@@ -31,10 +45,12 @@ export class RolesService extends ItemsService {
|
||||
knex: this.knex,
|
||||
accountability: this.accountability,
|
||||
});
|
||||
const presetsForRole = await presetsService.readByQuery({
|
||||
|
||||
const presetsForRole = (await presetsService.readByQuery({
|
||||
fields: ['id'],
|
||||
filter: { role: { _in: keys } },
|
||||
}) as { id: string }[];
|
||||
})) as { id: string }[];
|
||||
|
||||
const presetIDs = presetsForRole.map((preset) => preset.id);
|
||||
await presetsService.delete(presetIDs);
|
||||
|
||||
@@ -43,10 +59,12 @@ export class RolesService extends ItemsService {
|
||||
knex: this.knex,
|
||||
accountability: this.accountability,
|
||||
});
|
||||
const usersInRole = await usersService.readByQuery({
|
||||
|
||||
const usersInRole = (await usersService.readByQuery({
|
||||
fields: ['id'],
|
||||
filter: { role: { _in: keys } },
|
||||
}) as { id: string }[];
|
||||
})) as { id: string }[];
|
||||
|
||||
const userIDs = usersInRole.map((user) => user.id);
|
||||
await usersService.update({ status: 'suspended', role: null }, userIDs);
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@ import jwt from 'jsonwebtoken';
|
||||
import { sendInviteMail, sendPasswordResetMail } from '../mail';
|
||||
import database from '../database';
|
||||
import argon2 from 'argon2';
|
||||
import { InvalidPayloadException, ForbiddenException } from '../exceptions';
|
||||
import {
|
||||
InvalidPayloadException,
|
||||
ForbiddenException,
|
||||
UnprocessableEntityException,
|
||||
} from '../exceptions';
|
||||
import { Accountability, PrimaryKey, Item, AbstractServiceOptions } from '../types';
|
||||
import Knex from 'knex';
|
||||
import env from '../env';
|
||||
@@ -50,6 +54,30 @@ export class UsersService extends ItemsService {
|
||||
return this.service.update(data, key as any);
|
||||
}
|
||||
|
||||
delete(key: PrimaryKey): Promise<PrimaryKey>;
|
||||
delete(keys: PrimaryKey[]): Promise<PrimaryKey[]>;
|
||||
async delete(key: PrimaryKey | PrimaryKey[]): Promise<PrimaryKey | PrimaryKey[]> {
|
||||
const keys = Array.isArray(key) ? key : [key];
|
||||
|
||||
// Make sure there's at least one admin user left after this deletion is done
|
||||
const otherAdminUsers = await this.knex
|
||||
.count('*', { as: 'count' })
|
||||
.from('directus_users')
|
||||
.whereNotIn('directus_users.id', keys)
|
||||
.andWhere({ 'directus_roles.admin_access': true })
|
||||
.leftJoin('directus_roles', 'directus_users.role', 'directus_roles.id')
|
||||
.first();
|
||||
|
||||
const otherAdminUsersCount = +(otherAdminUsers?.count || 0);
|
||||
|
||||
if (otherAdminUsersCount === 0)
|
||||
throw new UnprocessableEntityException(`You can't delete the last admin user.`);
|
||||
|
||||
await super.delete(keys as any);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
async inviteUser(email: string, role: string) {
|
||||
await this.service.create({ email, role, status: 'invited' });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user