mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-19 17:55:17 -04:00
feat(auth): refactor auth, add oidc
Some checks are pending
Docker / build-and-push (frontend) (push) Waiting to run
Docker / build-and-push (backend) (push) Waiting to run
Deploy HD2 docs to Netlify / Deploys to netlify (push) Waiting to run
E2E Tests / backend-sqlite (push) Waiting to run
E2E Tests / backend-mariadb (push) Waiting to run
E2E Tests / backend-postgres (push) Waiting to run
E2E Tests / Build test build of frontend (push) Waiting to run
E2E Tests / frontend-cypress (1) (push) Blocked by required conditions
E2E Tests / frontend-cypress (2) (push) Blocked by required conditions
E2E Tests / frontend-cypress (3) (push) Blocked by required conditions
Lint and check format / Lint files and check formatting (push) Waiting to run
REUSE Compliance Check / reuse (push) Waiting to run
Scorecard supply-chain security / Scorecard analysis (push) Waiting to run
Static Analysis / Njsscan code scanning (push) Waiting to run
Static Analysis / CodeQL analysis (push) Waiting to run
Run tests & build / Test and build with NodeJS 20 (push) Waiting to run
Some checks are pending
Docker / build-and-push (frontend) (push) Waiting to run
Docker / build-and-push (backend) (push) Waiting to run
Deploy HD2 docs to Netlify / Deploys to netlify (push) Waiting to run
E2E Tests / backend-sqlite (push) Waiting to run
E2E Tests / backend-mariadb (push) Waiting to run
E2E Tests / backend-postgres (push) Waiting to run
E2E Tests / Build test build of frontend (push) Waiting to run
E2E Tests / frontend-cypress (1) (push) Blocked by required conditions
E2E Tests / frontend-cypress (2) (push) Blocked by required conditions
E2E Tests / frontend-cypress (3) (push) Blocked by required conditions
Lint and check format / Lint files and check formatting (push) Waiting to run
REUSE Compliance Check / reuse (push) Waiting to run
Scorecard supply-chain security / Scorecard analysis (push) Waiting to run
Static Analysis / Njsscan code scanning (push) Waiting to run
Static Analysis / CodeQL analysis (push) Waiting to run
Run tests & build / Test and build with NodeJS 20 (push) Waiting to run
Thanks to all HedgeDoc team members for the time discussing, helping with weird Nest issues, providing feedback and suggestions! Co-authored-by: Philip Molares <philip.molares@udo.edu> Signed-off-by: Philip Molares <philip.molares@udo.edu> Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
parent
1609f3e01f
commit
7f665fae4b
109 changed files with 2927 additions and 1700 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -8,123 +8,106 @@ import {
|
|||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Param,
|
||||
Post,
|
||||
Get,
|
||||
Put,
|
||||
Req,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { Session } from 'express-session';
|
||||
|
||||
import { IdentityService } from '../../../identity/identity.service';
|
||||
import { LdapLoginDto } from '../../../identity/ldap/ldap-login.dto';
|
||||
import { LdapAuthGuard } from '../../../identity/ldap/ldap.strategy';
|
||||
import { LocalAuthGuard } from '../../../identity/local/local.strategy';
|
||||
import { LoginDto } from '../../../identity/local/login.dto';
|
||||
import { RegisterDto } from '../../../identity/local/register.dto';
|
||||
import { UpdatePasswordDto } from '../../../identity/local/update-password.dto';
|
||||
import { SessionGuard } from '../../../identity/session.guard';
|
||||
import { OidcService } from '../../../identity/oidc/oidc.service';
|
||||
import { PendingUserConfirmationDto } from '../../../identity/pending-user-confirmation.dto';
|
||||
import { ProviderType } from '../../../identity/provider-type.enum';
|
||||
import {
|
||||
RequestWithSession,
|
||||
SessionGuard,
|
||||
} from '../../../identity/session.guard';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { SessionState } from '../../../sessions/session.service';
|
||||
import { User } from '../../../users/user.entity';
|
||||
import { UsersService } from '../../../users/users.service';
|
||||
import { makeUsernameLowercase } from '../../../utils/username';
|
||||
import { LoginEnabledGuard } from '../../utils/login-enabled.guard';
|
||||
import { FullUserInfoDto } from '../../../users/user-info.dto';
|
||||
import { OpenApi } from '../../utils/openapi.decorator';
|
||||
import { RegistrationEnabledGuard } from '../../utils/registration-enabled.guard';
|
||||
import { RequestUser } from '../../utils/request-user.decorator';
|
||||
|
||||
type RequestWithSession = Request & {
|
||||
session: SessionState;
|
||||
};
|
||||
|
||||
@ApiTags('auth')
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(
|
||||
private readonly logger: ConsoleLoggerService,
|
||||
private usersService: UsersService,
|
||||
private identityService: IdentityService,
|
||||
private oidcService: OidcService,
|
||||
) {
|
||||
this.logger.setContext(AuthController.name);
|
||||
}
|
||||
|
||||
@UseGuards(RegistrationEnabledGuard)
|
||||
@Post('local')
|
||||
@OpenApi(201, 400, 403, 409)
|
||||
async registerUser(
|
||||
@Req() request: RequestWithSession,
|
||||
@Body() registerDto: RegisterDto,
|
||||
): Promise<void> {
|
||||
await this.identityService.checkPasswordStrength(registerDto.password);
|
||||
const user = await this.usersService.createUser(
|
||||
registerDto.username,
|
||||
registerDto.displayName,
|
||||
);
|
||||
await this.identityService.createLocalIdentity(user, registerDto.password);
|
||||
request.session.username = registerDto.username;
|
||||
request.session.authProvider = 'local';
|
||||
}
|
||||
|
||||
@UseGuards(LoginEnabledGuard, SessionGuard)
|
||||
@Put('local')
|
||||
@OpenApi(200, 400, 401)
|
||||
async updatePassword(
|
||||
@RequestUser() user: User,
|
||||
@Body() changePasswordDto: UpdatePasswordDto,
|
||||
): Promise<void> {
|
||||
await this.identityService.checkLocalPassword(
|
||||
user,
|
||||
changePasswordDto.currentPassword,
|
||||
);
|
||||
await this.identityService.updateLocalPassword(
|
||||
user,
|
||||
changePasswordDto.newPassword,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@UseGuards(LoginEnabledGuard, LocalAuthGuard)
|
||||
@Post('local/login')
|
||||
@OpenApi(201, 400, 401)
|
||||
login(
|
||||
@Req()
|
||||
request: RequestWithSession,
|
||||
@Body() loginDto: LoginDto,
|
||||
): void {
|
||||
// There is no further testing needed as we only get to this point if LocalAuthGuard was successful
|
||||
request.session.username = loginDto.username;
|
||||
request.session.authProvider = 'local';
|
||||
}
|
||||
|
||||
@UseGuards(LdapAuthGuard)
|
||||
@Post('ldap/:ldapIdentifier')
|
||||
@OpenApi(201, 400, 401)
|
||||
loginWithLdap(
|
||||
@Req()
|
||||
request: RequestWithSession,
|
||||
@Param('ldapIdentifier') ldapIdentifier: string,
|
||||
@Body() loginDto: LdapLoginDto,
|
||||
): void {
|
||||
// There is no further testing needed as we only get to this point if LdapAuthGuard was successful
|
||||
request.session.username = makeUsernameLowercase(loginDto.username);
|
||||
request.session.authProvider = 'ldap';
|
||||
}
|
||||
|
||||
@UseGuards(SessionGuard)
|
||||
@Delete('logout')
|
||||
@OpenApi(204, 400, 401)
|
||||
logout(@Req() request: Request & { session: Session }): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
request.session.destroy((err) => {
|
||||
if (err) {
|
||||
this.logger.error('Encountered an error while logging out: ${err}');
|
||||
reject(new BadRequestException('Unable to log out'));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
@OpenApi(200, 400, 401)
|
||||
logout(@Req() request: RequestWithSession): { redirect: string } {
|
||||
let logoutUrl: string | null = null;
|
||||
if (request.session.authProviderType === ProviderType.OIDC) {
|
||||
logoutUrl = this.oidcService.getLogoutUrl(request);
|
||||
}
|
||||
request.session.destroy((err) => {
|
||||
if (err) {
|
||||
this.logger.error(
|
||||
'Error during logout:' + String(err),
|
||||
undefined,
|
||||
'logout',
|
||||
);
|
||||
throw new BadRequestException('Unable to log out');
|
||||
}
|
||||
});
|
||||
return {
|
||||
redirect: logoutUrl || '/',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('pending-user')
|
||||
@OpenApi(200, 400)
|
||||
getPendingUserData(
|
||||
@Req() request: RequestWithSession,
|
||||
): Partial<FullUserInfoDto> {
|
||||
if (
|
||||
!request.session.newUserData ||
|
||||
!request.session.authProviderIdentifier ||
|
||||
!request.session.authProviderType
|
||||
) {
|
||||
throw new BadRequestException('No pending user data');
|
||||
}
|
||||
return request.session.newUserData;
|
||||
}
|
||||
|
||||
@Put('pending-user')
|
||||
@OpenApi(204, 400)
|
||||
async confirmPendingUserData(
|
||||
@Req() request: RequestWithSession,
|
||||
@Body() updatedUserInfo: PendingUserConfirmationDto,
|
||||
): Promise<void> {
|
||||
if (
|
||||
!request.session.newUserData ||
|
||||
!request.session.authProviderIdentifier ||
|
||||
!request.session.authProviderType ||
|
||||
!request.session.providerUserId
|
||||
) {
|
||||
throw new BadRequestException('No pending user data');
|
||||
}
|
||||
const identity = await this.identityService.createUserWithIdentity(
|
||||
request.session.newUserData,
|
||||
updatedUserInfo,
|
||||
request.session.authProviderType,
|
||||
request.session.authProviderIdentifier,
|
||||
request.session.providerUserId,
|
||||
);
|
||||
request.session.username = (await identity.user).username;
|
||||
// Cleanup
|
||||
request.session.newUserData = undefined;
|
||||
}
|
||||
|
||||
@Delete('pending-user')
|
||||
@OpenApi(204, 400)
|
||||
deletePendingUserData(@Req() request: RequestWithSession): void {
|
||||
request.session.newUserData = undefined;
|
||||
request.session.authProviderIdentifier = undefined;
|
||||
request.session.authProviderType = undefined;
|
||||
request.session.providerUserId = undefined;
|
||||
}
|
||||
}
|
||||
|
|
84
backend/src/api/private/auth/ldap/ldap.controller.ts
Normal file
84
backend/src/api/private/auth/ldap/ldap.controller.ts
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
InternalServerErrorException,
|
||||
Param,
|
||||
Post,
|
||||
Req,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { NotInDBError } from '../../../../errors/errors';
|
||||
import { IdentityService } from '../../../../identity/identity.service';
|
||||
import { LdapLoginDto } from '../../../../identity/ldap/ldap-login.dto';
|
||||
import { LdapService } from '../../../../identity/ldap/ldap.service';
|
||||
import { ProviderType } from '../../../../identity/provider-type.enum';
|
||||
import { RequestWithSession } from '../../../../identity/session.guard';
|
||||
import { ConsoleLoggerService } from '../../../../logger/console-logger.service';
|
||||
import { UsersService } from '../../../../users/users.service';
|
||||
import { makeUsernameLowercase } from '../../../../utils/username';
|
||||
import { OpenApi } from '../../../utils/openapi.decorator';
|
||||
|
||||
@ApiTags('auth')
|
||||
@Controller('/auth/ldap')
|
||||
export class LdapController {
|
||||
constructor(
|
||||
private readonly logger: ConsoleLoggerService,
|
||||
private usersService: UsersService,
|
||||
private ldapService: LdapService,
|
||||
private identityService: IdentityService,
|
||||
) {
|
||||
this.logger.setContext(LdapController.name);
|
||||
}
|
||||
|
||||
@Post(':ldapIdentifier/login')
|
||||
@OpenApi(200, 400, 401)
|
||||
async loginWithLdap(
|
||||
@Req()
|
||||
request: RequestWithSession,
|
||||
@Param('ldapIdentifier') ldapIdentifier: string,
|
||||
@Body() loginDto: LdapLoginDto,
|
||||
): Promise<{ newUser: boolean }> {
|
||||
const ldapConfig = this.ldapService.getLdapConfig(ldapIdentifier);
|
||||
const userInfo = await this.ldapService.getUserInfoFromLdap(
|
||||
ldapConfig,
|
||||
loginDto.username,
|
||||
loginDto.password,
|
||||
);
|
||||
try {
|
||||
request.session.authProviderType = ProviderType.LDAP;
|
||||
request.session.authProviderIdentifier = ldapIdentifier;
|
||||
request.session.providerUserId = userInfo.id;
|
||||
await this.identityService.getIdentityFromUserIdAndProviderType(
|
||||
userInfo.id,
|
||||
ProviderType.LDAP,
|
||||
ldapIdentifier,
|
||||
);
|
||||
if (this.identityService.mayUpdateIdentity(ldapIdentifier)) {
|
||||
const user = await this.usersService.getUserByUsername(
|
||||
makeUsernameLowercase(loginDto.username),
|
||||
);
|
||||
await this.usersService.updateUser(
|
||||
user,
|
||||
userInfo.displayName,
|
||||
userInfo.email,
|
||||
userInfo.photoUrl,
|
||||
);
|
||||
}
|
||||
request.session.username = makeUsernameLowercase(loginDto.username);
|
||||
return { newUser: false };
|
||||
} catch (error) {
|
||||
if (error instanceof NotInDBError) {
|
||||
request.session.newUserData = userInfo;
|
||||
return { newUser: true };
|
||||
}
|
||||
this.logger.error(`Error during LDAP login: ${String(error)}`);
|
||||
throw new InternalServerErrorException('Error during LDAP login');
|
||||
}
|
||||
}
|
||||
}
|
104
backend/src/api/private/auth/local/local.controller.ts
Normal file
104
backend/src/api/private/auth/local/local.controller.ts
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Post,
|
||||
Put,
|
||||
Req,
|
||||
UnauthorizedException,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { LocalService } from '../../../../identity/local/local.service';
|
||||
import { LoginDto } from '../../../../identity/local/login.dto';
|
||||
import { RegisterDto } from '../../../../identity/local/register.dto';
|
||||
import { UpdatePasswordDto } from '../../../../identity/local/update-password.dto';
|
||||
import { ProviderType } from '../../../../identity/provider-type.enum';
|
||||
import {
|
||||
RequestWithSession,
|
||||
SessionGuard,
|
||||
} from '../../../../identity/session.guard';
|
||||
import { ConsoleLoggerService } from '../../../../logger/console-logger.service';
|
||||
import { User } from '../../../../users/user.entity';
|
||||
import { UsersService } from '../../../../users/users.service';
|
||||
import { LoginEnabledGuard } from '../../../utils/login-enabled.guard';
|
||||
import { OpenApi } from '../../../utils/openapi.decorator';
|
||||
import { RegistrationEnabledGuard } from '../../../utils/registration-enabled.guard';
|
||||
import { RequestUser } from '../../../utils/request-user.decorator';
|
||||
|
||||
@ApiTags('auth')
|
||||
@Controller('/auth/local')
|
||||
export class LocalController {
|
||||
constructor(
|
||||
private readonly logger: ConsoleLoggerService,
|
||||
private usersService: UsersService,
|
||||
private localIdentityService: LocalService,
|
||||
) {
|
||||
this.logger.setContext(LocalController.name);
|
||||
}
|
||||
|
||||
@UseGuards(RegistrationEnabledGuard)
|
||||
@Post()
|
||||
@OpenApi(201, 400, 403, 409)
|
||||
async registerUser(
|
||||
@Req() request: RequestWithSession,
|
||||
@Body() registerDto: RegisterDto,
|
||||
): Promise<void> {
|
||||
await this.localIdentityService.checkPasswordStrength(registerDto.password);
|
||||
const user = await this.usersService.createUser(
|
||||
registerDto.username,
|
||||
registerDto.displayName,
|
||||
);
|
||||
await this.localIdentityService.createLocalIdentity(
|
||||
user,
|
||||
registerDto.password,
|
||||
);
|
||||
// Log the user in after registration
|
||||
request.session.authProviderType = ProviderType.LOCAL;
|
||||
request.session.username = registerDto.username;
|
||||
}
|
||||
|
||||
@UseGuards(LoginEnabledGuard, SessionGuard)
|
||||
@Put()
|
||||
@OpenApi(200, 400, 401)
|
||||
async updatePassword(
|
||||
@RequestUser() user: User,
|
||||
@Body() changePasswordDto: UpdatePasswordDto,
|
||||
): Promise<void> {
|
||||
await this.localIdentityService.checkLocalPassword(
|
||||
user,
|
||||
changePasswordDto.currentPassword,
|
||||
);
|
||||
await this.localIdentityService.updateLocalPassword(
|
||||
user,
|
||||
changePasswordDto.newPassword,
|
||||
);
|
||||
}
|
||||
|
||||
@UseGuards(LoginEnabledGuard)
|
||||
@Post('login')
|
||||
@OpenApi(201, 400, 401)
|
||||
async login(
|
||||
@Req()
|
||||
request: RequestWithSession,
|
||||
@Body() loginDto: LoginDto,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const user = await this.usersService.getUserByUsername(loginDto.username);
|
||||
await this.localIdentityService.checkLocalPassword(
|
||||
user,
|
||||
loginDto.password,
|
||||
);
|
||||
request.session.username = loginDto.username;
|
||||
request.session.authProviderType = ProviderType.LOCAL;
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to log in user: ${String(error)}`);
|
||||
throw new UnauthorizedException('Invalid username or password');
|
||||
}
|
||||
}
|
||||
}
|
101
backend/src/api/private/auth/oidc/oidc.controller.ts
Normal file
101
backend/src/api/private/auth/oidc/oidc.controller.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Redirect,
|
||||
Req,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { IdentityService } from '../../../../identity/identity.service';
|
||||
import { OidcService } from '../../../../identity/oidc/oidc.service';
|
||||
import { ProviderType } from '../../../../identity/provider-type.enum';
|
||||
import { RequestWithSession } from '../../../../identity/session.guard';
|
||||
import { ConsoleLoggerService } from '../../../../logger/console-logger.service';
|
||||
import { UsersService } from '../../../../users/users.service';
|
||||
import { OpenApi } from '../../../utils/openapi.decorator';
|
||||
|
||||
@ApiTags('auth')
|
||||
@Controller('/auth/oidc')
|
||||
export class OidcController {
|
||||
constructor(
|
||||
private readonly logger: ConsoleLoggerService,
|
||||
private usersService: UsersService,
|
||||
private identityService: IdentityService,
|
||||
private oidcService: OidcService,
|
||||
) {
|
||||
this.logger.setContext(OidcController.name);
|
||||
}
|
||||
|
||||
@Get(':oidcIdentifier')
|
||||
@Redirect()
|
||||
@OpenApi(201, 400, 401)
|
||||
loginWithOpenIdConnect(
|
||||
@Req() request: RequestWithSession,
|
||||
@Param('oidcIdentifier') oidcIdentifier: string,
|
||||
): { url: string } {
|
||||
const code = this.oidcService.generateCode();
|
||||
request.session.oidcLoginCode = code;
|
||||
request.session.authProviderType = ProviderType.OIDC;
|
||||
request.session.authProviderIdentifier = oidcIdentifier;
|
||||
const authorizationUrl = this.oidcService.getAuthorizationUrl(
|
||||
oidcIdentifier,
|
||||
code,
|
||||
);
|
||||
return { url: authorizationUrl };
|
||||
}
|
||||
|
||||
@Get(':oidcIdentifier/callback')
|
||||
@Redirect()
|
||||
@OpenApi(201, 400, 401)
|
||||
async callback(
|
||||
@Param('oidcIdentifier') oidcIdentifier: string,
|
||||
@Req() request: RequestWithSession,
|
||||
): Promise<{ url: string }> {
|
||||
try {
|
||||
const userInfo = await this.oidcService.extractUserInfoFromCallback(
|
||||
oidcIdentifier,
|
||||
request,
|
||||
);
|
||||
const oidcUserIdentifier = request.session.providerUserId;
|
||||
if (!oidcUserIdentifier) {
|
||||
throw new Error('No OIDC user identifier found');
|
||||
}
|
||||
const identity = await this.oidcService.getExistingOidcIdentity(
|
||||
oidcIdentifier,
|
||||
oidcUserIdentifier,
|
||||
);
|
||||
request.session.authProviderType = ProviderType.OIDC;
|
||||
const mayUpdate = this.identityService.mayUpdateIdentity(oidcIdentifier);
|
||||
if (identity !== null) {
|
||||
const user = await identity.user;
|
||||
if (mayUpdate) {
|
||||
await this.usersService.updateUser(
|
||||
user,
|
||||
userInfo.displayName,
|
||||
userInfo.email,
|
||||
userInfo.photoUrl,
|
||||
);
|
||||
}
|
||||
|
||||
request.session.username = user.username;
|
||||
return { url: '/' };
|
||||
} else {
|
||||
request.session.newUserData = userInfo;
|
||||
return { url: '/new-user' };
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.log(
|
||||
'Error during OIDC callback:' + String(error),
|
||||
'callback',
|
||||
);
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -75,6 +75,11 @@ export class MeController {
|
|||
@RequestUser() user: User,
|
||||
@Body('displayName') newDisplayName: string,
|
||||
): Promise<void> {
|
||||
await this.userService.changeDisplayName(user, newDisplayName);
|
||||
await this.userService.updateUser(
|
||||
user,
|
||||
newDisplayName,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -18,6 +18,9 @@ import { RevisionsModule } from '../../revisions/revisions.module';
|
|||
import { UsersModule } from '../../users/users.module';
|
||||
import { AliasController } from './alias/alias.controller';
|
||||
import { AuthController } from './auth/auth.controller';
|
||||
import { LdapController } from './auth/ldap/ldap.controller';
|
||||
import { LocalController } from './auth/local/local.controller';
|
||||
import { OidcController } from './auth/oidc/oidc.controller';
|
||||
import { ConfigController } from './config/config.controller';
|
||||
import { GroupsController } from './groups/groups.controller';
|
||||
import { HistoryController } from './me/history/history.controller';
|
||||
|
@ -52,6 +55,9 @@ import { UsersController } from './users/users.controller';
|
|||
AuthController,
|
||||
UsersController,
|
||||
GroupsController,
|
||||
LdapController,
|
||||
LocalController,
|
||||
OidcController,
|
||||
],
|
||||
})
|
||||
export class PrivateApiModule {}
|
||||
|
|
|
@ -3,11 +3,15 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { Controller, Get, Param } from '@nestjs/common';
|
||||
import { Body, Controller, Get, HttpCode, Param, Post } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import { UserInfoDto } from '../../../users/user-info.dto';
|
||||
import {
|
||||
UsernameCheckDto,
|
||||
UsernameCheckResponseDto,
|
||||
} from '../../../users/username-check.dto';
|
||||
import { UsersService } from '../../../users/users.service';
|
||||
import { Username } from '../../../utils/username';
|
||||
import { OpenApi } from '../../utils/openapi.decorator';
|
||||
|
@ -22,7 +26,20 @@ export class UsersController {
|
|||
this.logger.setContext(UsersController.name);
|
||||
}
|
||||
|
||||
@Get(':username')
|
||||
@Post('check')
|
||||
@HttpCode(200)
|
||||
@OpenApi(200)
|
||||
async checkUsername(
|
||||
@Body() usernameCheck: UsernameCheckDto,
|
||||
): Promise<UsernameCheckResponseDto> {
|
||||
const userExists = await this.userService.checkIfUserExists(
|
||||
usernameCheck.username,
|
||||
);
|
||||
// TODO Check if username is blocked
|
||||
return { usernameAvailable: !userExists };
|
||||
}
|
||||
|
||||
@Get('profile/:username')
|
||||
@OpenApi(200)
|
||||
async getUser(@Param('username') username: Username): Promise<UserInfoDto> {
|
||||
return this.userService.toUserDto(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -20,12 +20,12 @@ import { CompleteRequest } from './request.type';
|
|||
export const SessionAuthProvider = createParamDecorator(
|
||||
(data: unknown, ctx: ExecutionContext) => {
|
||||
const request: CompleteRequest = ctx.switchToHttp().getRequest();
|
||||
if (!request.session?.authProvider) {
|
||||
if (!request.session?.authProviderType) {
|
||||
// We should have an auth provider here, otherwise something is wrong
|
||||
throw new InternalServerErrorException(
|
||||
'Session is missing an auth provider identifier',
|
||||
);
|
||||
}
|
||||
return request.session.authProvider;
|
||||
return request.session.authProviderType;
|
||||
},
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue