mirror of
https://github.com/stonith404/pingvin-share.git
synced 2024-11-15 03:50:11 +01:00
fix: prevent deletion of last admin account
This commit is contained in:
parent
4ce64206be
commit
e1a5d19544
@ -3,6 +3,7 @@ import {
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
HttpCode,
|
||||
Param,
|
||||
Patch,
|
||||
Post,
|
||||
@ -14,18 +15,18 @@ import { Response } from "express";
|
||||
import { GetUser } from "src/auth/decorator/getUser.decorator";
|
||||
import { AdministratorGuard } from "src/auth/guard/isAdmin.guard";
|
||||
import { JwtGuard } from "src/auth/guard/jwt.guard";
|
||||
import { ConfigService } from "../config/config.service";
|
||||
import { CreateUserDTO } from "./dto/createUser.dto";
|
||||
import { UpdateOwnUserDTO } from "./dto/updateOwnUser.dto";
|
||||
import { UpdateUserDto } from "./dto/updateUser.dto";
|
||||
import { UserDTO } from "./dto/user.dto";
|
||||
import { UserSevice } from "./user.service";
|
||||
import { ConfigService } from "../config/config.service";
|
||||
|
||||
@Controller("users")
|
||||
export class UserController {
|
||||
constructor(
|
||||
private userService: UserSevice,
|
||||
private config: ConfigService,
|
||||
private config: ConfigService
|
||||
) {}
|
||||
|
||||
// Own user operations
|
||||
@ -42,17 +43,20 @@ export class UserController {
|
||||
@UseGuards(JwtGuard)
|
||||
async updateCurrentUser(
|
||||
@GetUser() user: User,
|
||||
@Body() data: UpdateOwnUserDTO,
|
||||
@Body() data: UpdateOwnUserDTO
|
||||
) {
|
||||
return new UserDTO().from(await this.userService.update(user.id, data));
|
||||
}
|
||||
|
||||
@Delete("me")
|
||||
@HttpCode(204)
|
||||
@UseGuards(JwtGuard)
|
||||
async deleteCurrentUser(
|
||||
@GetUser() user: User,
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
@Res({ passthrough: true }) response: Response
|
||||
) {
|
||||
await this.userService.delete(user.id);
|
||||
|
||||
const isSecure = this.config.get("general.secureCookies");
|
||||
|
||||
response.cookie("access_token", "accessToken", {
|
||||
@ -65,7 +69,6 @@ export class UserController {
|
||||
maxAge: -1,
|
||||
secure: isSecure,
|
||||
});
|
||||
return new UserDTO().from(await this.userService.delete(user.id));
|
||||
}
|
||||
|
||||
// Global user operations
|
||||
|
@ -2,15 +2,15 @@ import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
||||
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
|
||||
import * as argon from "argon2";
|
||||
import * as crypto from "crypto";
|
||||
import { Entry } from "ldapts";
|
||||
import { AuthSignInDTO } from "src/auth/dto/authSignIn.dto";
|
||||
import { EmailService } from "src/email/email.service";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { inspect } from "util";
|
||||
import { ConfigService } from "../config/config.service";
|
||||
import { FileService } from "../file/file.service";
|
||||
import { CreateUserDTO } from "./dto/createUser.dto";
|
||||
import { UpdateUserDto } from "./dto/updateUser.dto";
|
||||
import { ConfigService } from "../config/config.service";
|
||||
import { Entry } from "ldapts";
|
||||
import { AuthSignInDTO } from "src/auth/dto/authSignIn.dto";
|
||||
import { inspect } from "util";
|
||||
|
||||
@Injectable()
|
||||
export class UserSevice {
|
||||
@ -20,7 +20,7 @@ export class UserSevice {
|
||||
private prisma: PrismaService,
|
||||
private emailService: EmailService,
|
||||
private fileService: FileService,
|
||||
private configService: ConfigService,
|
||||
private configService: ConfigService
|
||||
) {}
|
||||
|
||||
async list() {
|
||||
@ -55,7 +55,7 @@ export class UserSevice {
|
||||
if (e.code == "P2002") {
|
||||
const duplicatedField: string = e.meta.target[0];
|
||||
throw new BadRequestException(
|
||||
`A user with this ${duplicatedField} already exists`,
|
||||
`A user with this ${duplicatedField} already exists`
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -75,7 +75,7 @@ export class UserSevice {
|
||||
if (e.code == "P2002") {
|
||||
const duplicatedField: string = e.meta.target[0];
|
||||
throw new BadRequestException(
|
||||
`A user with this ${duplicatedField} already exists`,
|
||||
`A user with this ${duplicatedField} already exists`
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -89,8 +89,18 @@ export class UserSevice {
|
||||
});
|
||||
if (!user) throw new BadRequestException("User not found");
|
||||
|
||||
if (user.isAdmin) {
|
||||
const userCount = await this.prisma.user.count({
|
||||
where: { isAdmin: true },
|
||||
});
|
||||
|
||||
if (userCount === 1) {
|
||||
throw new BadRequestException("Cannot delete the last admin user");
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
user.shares.map((share) => this.fileService.deleteAllFiles(share.id)),
|
||||
user.shares.map((share) => this.fileService.deleteAllFiles(share.id))
|
||||
);
|
||||
|
||||
return await this.prisma.user.delete({ where: { id } });
|
||||
@ -98,7 +108,7 @@ export class UserSevice {
|
||||
|
||||
async findOrCreateFromLDAP(
|
||||
providedCredentials: AuthSignInDTO,
|
||||
ldapEntry: Entry,
|
||||
ldapEntry: Entry
|
||||
) {
|
||||
const fieldNameMemberOf = this.configService.get("ldap.fieldNameMemberOf");
|
||||
const fieldNameEmail = this.configService.get("ldap.fieldNameEmail");
|
||||
@ -112,7 +122,7 @@ export class UserSevice {
|
||||
isAdmin = entryGroups.includes(adminGroup) ?? false;
|
||||
} else {
|
||||
this.logger.warn(
|
||||
`Trying to create/update a ldap user but the member field ${fieldNameMemberOf} is not present.`,
|
||||
`Trying to create/update a ldap user but the member field ${fieldNameMemberOf} is not present.`
|
||||
);
|
||||
}
|
||||
|
||||
@ -126,7 +136,7 @@ export class UserSevice {
|
||||
}
|
||||
} else {
|
||||
this.logger.warn(
|
||||
`Trying to create/update a ldap user but the email field ${fieldNameEmail} is not present.`,
|
||||
`Trying to create/update a ldap user but the email field ${fieldNameEmail} is not present.`
|
||||
);
|
||||
}
|
||||
|
||||
@ -174,7 +184,7 @@ export class UserSevice {
|
||||
})
|
||||
.catch((error) => {
|
||||
this.logger.warn(
|
||||
`Failed to update users ${user.id} placeholder username: ${inspect(error)}`,
|
||||
`Failed to update users ${user.id} placeholder username: ${inspect(error)}`
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -192,13 +202,13 @@ export class UserSevice {
|
||||
})
|
||||
.then((newUser) => {
|
||||
this.logger.log(
|
||||
`Updated users ${user.id} email from ldap from ${user.email} to ${userEmail}.`,
|
||||
`Updated users ${user.id} email from ldap from ${user.email} to ${userEmail}.`
|
||||
);
|
||||
user.email = newUser.email;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.logger.error(
|
||||
`Failed to update users ${user.id} email to ${userEmail}: ${inspect(error)}`,
|
||||
`Failed to update users ${user.id} email to ${userEmail}: ${inspect(error)}`
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -209,7 +219,7 @@ export class UserSevice {
|
||||
if (e.code == "P2002") {
|
||||
const duplicatedField: string = e.meta.target[0];
|
||||
throw new BadRequestException(
|
||||
`A user with this ${duplicatedField} already exists`,
|
||||
`A user with this ${duplicatedField} already exists`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ const Account = () => {
|
||||
username: yup
|
||||
.string()
|
||||
.min(3, t("common.error.too-short", { length: 3 })),
|
||||
}),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
@ -79,7 +79,7 @@ const Account = () => {
|
||||
.string()
|
||||
.min(8, t("common.error.too-short", { length: 8 }))
|
||||
.required(t("common.error.field-required")),
|
||||
}),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
@ -93,7 +93,7 @@ const Account = () => {
|
||||
.string()
|
||||
.min(8, t("common.error.too-short", { length: 8 }))
|
||||
.required(t("common.error.field-required")),
|
||||
}),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
@ -110,7 +110,7 @@ const Account = () => {
|
||||
.min(6, t("common.error.exact-length", { length: 6 }))
|
||||
.max(6, t("common.error.exact-length", { length: 6 }))
|
||||
.matches(/^[0-9]+$/, { message: t("common.error.invalid-number") }),
|
||||
}),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
@ -155,7 +155,7 @@ const Account = () => {
|
||||
email: values.email,
|
||||
})
|
||||
.then(() => toast.success(t("account.notify.info.success")))
|
||||
.catch(toast.axiosError),
|
||||
.catch(toast.axiosError)
|
||||
)}
|
||||
>
|
||||
<Stack>
|
||||
@ -193,7 +193,7 @@ const Account = () => {
|
||||
toast.success(t("account.notify.password.success"));
|
||||
passwordForm.reset();
|
||||
})
|
||||
.catch(toast.axiosError),
|
||||
.catch(toast.axiosError)
|
||||
)}
|
||||
>
|
||||
<Stack>
|
||||
@ -265,7 +265,7 @@ const Account = () => {
|
||||
unlinkOAuth(provider)
|
||||
.then(() => {
|
||||
toast.success(
|
||||
t("account.notify.oauth.unlinked.success"),
|
||||
t("account.notify.oauth.unlinked.success")
|
||||
);
|
||||
refreshOAuthStatus();
|
||||
})
|
||||
@ -281,7 +281,7 @@ const Account = () => {
|
||||
component="a"
|
||||
href={getOAuthUrl(
|
||||
config.get("general.appUrl"),
|
||||
provider,
|
||||
provider
|
||||
)}
|
||||
>
|
||||
{t("account.card.oauth.link")}
|
||||
@ -324,7 +324,7 @@ const Account = () => {
|
||||
<Stack>
|
||||
<PasswordInput
|
||||
description={t(
|
||||
"account.card.security.totp.disable.description",
|
||||
"account.card.security.totp.disable.description"
|
||||
)}
|
||||
label={t("account.card.password.title")}
|
||||
{...disableTotpForm.getInputProps("password")}
|
||||
@ -366,7 +366,7 @@ const Account = () => {
|
||||
<PasswordInput
|
||||
label={t("account.card.password.title")}
|
||||
description={t(
|
||||
"account.card.security.totp.enable.description",
|
||||
"account.card.security.totp.enable.description"
|
||||
)}
|
||||
{...enableTotpForm.getInputProps("password")}
|
||||
/>
|
||||
@ -414,8 +414,10 @@ const Account = () => {
|
||||
},
|
||||
confirmProps: { color: "red" },
|
||||
onConfirm: async () => {
|
||||
await userService.removeCurrentUser();
|
||||
window.location.reload();
|
||||
await userService
|
||||
.removeCurrentUser()
|
||||
.then(window.location.reload)
|
||||
.catch(toast.axiosError);
|
||||
},
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user