1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-01 08:31:59 +00:00

refactor(server): merge auth guards to authentication guard (#877)

This commit is contained in:
Jason Rasmussen 2022-10-28 14:57:52 -04:00 committed by GitHub
parent 9614da6238
commit 443c842723
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 46 additions and 63 deletions

View file

@ -6,7 +6,6 @@ import {
Patch, Patch,
Param, Param,
Delete, Delete,
UseGuards,
ValidationPipe, ValidationPipe,
ParseUUIDPipe, ParseUUIDPipe,
Put, Put,
@ -15,7 +14,7 @@ import {
import { ParseMeUUIDPipe } from '../validation/parse-me-uuid-pipe'; import { ParseMeUUIDPipe } from '../validation/parse-me-uuid-pipe';
import { AlbumService } from './album.service'; import { AlbumService } from './album.service';
import { CreateAlbumDto } from './dto/create-album.dto'; import { CreateAlbumDto } from './dto/create-album.dto';
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard'; import { Authenticated } from '../../decorators/authenticated.decorator';
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
import { AddAssetsDto } from './dto/add-assets.dto'; import { AddAssetsDto } from './dto/add-assets.dto';
import { AddUsersDto } from './dto/add-users.dto'; import { AddUsersDto } from './dto/add-users.dto';
@ -27,7 +26,7 @@ import { AlbumResponseDto } from './response-dto/album-response.dto';
import { AlbumCountResponseDto } from './response-dto/album-count-response.dto'; import { AlbumCountResponseDto } from './response-dto/album-count-response.dto';
// TODO might be worth creating a AlbumParamsDto that validates `albumId` instead of using the pipe. // TODO might be worth creating a AlbumParamsDto that validates `albumId` instead of using the pipe.
@UseGuards(JwtAuthGuard) @Authenticated()
@ApiBearerAuth() @ApiBearerAuth()
@ApiTags('Album') @ApiTags('Album')
@Controller('album') @Controller('album')

View file

@ -3,7 +3,6 @@ import {
Post, Post,
UseInterceptors, UseInterceptors,
Body, Body,
UseGuards,
Get, Get,
Param, Param,
ValidationPipe, ValidationPipe,
@ -17,7 +16,7 @@ import {
UploadedFile, UploadedFile,
Header, Header,
} from '@nestjs/common'; } from '@nestjs/common';
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard'; import { Authenticated } from '../../decorators/authenticated.decorator';
import { AssetService } from './asset.service'; import { AssetService } from './asset.service';
import { FileInterceptor } from '@nestjs/platform-express'; import { FileInterceptor } from '@nestjs/platform-express';
import { assetUploadOption } from '../../config/asset-upload.config'; import { assetUploadOption } from '../../config/asset-upload.config';
@ -52,7 +51,7 @@ import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-use
import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto'; import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto'; import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto';
@UseGuards(JwtAuthGuard) @Authenticated()
@ApiBearerAuth() @ApiBearerAuth()
@ApiTags('Asset') @ApiTags('Asset')
@Controller('asset') @Controller('asset')

View file

@ -1,7 +1,7 @@
import { Body, Controller, Post, Res, UseGuards, ValidationPipe, Ip } from '@nestjs/common'; import { Body, Controller, Post, Res, ValidationPipe, Ip } from '@nestjs/common';
import { ApiBadRequestResponse, ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { ApiBadRequestResponse, ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard'; import { Authenticated } from '../../decorators/authenticated.decorator';
import { AuthService } from './auth.service'; import { AuthService } from './auth.service';
import { LoginCredentialDto } from './dto/login-credential.dto'; import { LoginCredentialDto } from './dto/login-credential.dto';
import { LoginResponseDto } from './response-dto/login-response.dto'; import { LoginResponseDto } from './response-dto/login-response.dto';
@ -42,7 +42,7 @@ export class AuthController {
return await this.authService.adminSignUp(signUpCredential); return await this.authService.adminSignUp(signUpCredential);
} }
@UseGuards(JwtAuthGuard) @Authenticated()
@ApiBearerAuth() @ApiBearerAuth()
@Post('/validateToken') @Post('/validateToken')
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars

View file

@ -1,13 +1,13 @@
import { Controller, Post, Body, Patch, UseGuards, ValidationPipe } from '@nestjs/common'; import { Controller, Post, Body, Patch, ValidationPipe } from '@nestjs/common';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard'; import { Authenticated } from '../../decorators/authenticated.decorator';
import { DeviceInfoService } from './device-info.service'; import { DeviceInfoService } from './device-info.service';
import { CreateDeviceInfoDto } from './dto/create-device-info.dto'; import { CreateDeviceInfoDto } from './dto/create-device-info.dto';
import { UpdateDeviceInfoDto } from './dto/update-device-info.dto'; import { UpdateDeviceInfoDto } from './dto/update-device-info.dto';
import { DeviceInfoResponseDto } from './response-dto/create-device-info-response.dto'; import { DeviceInfoResponseDto } from './response-dto/create-device-info-response.dto';
@UseGuards(JwtAuthGuard) @Authenticated()
@ApiBearerAuth() @ApiBearerAuth()
@ApiTags('Device Info') @ApiTags('Device Info')
@Controller('device-info') @Controller('device-info')

View file

@ -1,16 +1,14 @@
import { Controller, Get, Body, UseGuards, ValidationPipe, Put, Param } from '@nestjs/common'; import { Controller, Get, Body, ValidationPipe, Put, Param } from '@nestjs/common';
import { JobService } from './job.service'; import { JobService } from './job.service';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard'; import { Authenticated } from '../../decorators/authenticated.decorator';
import { AdminRolesGuard } from '../../middlewares/admin-role-guard.middleware';
import { AllJobStatusResponseDto } from './response-dto/all-job-status-response.dto'; import { AllJobStatusResponseDto } from './response-dto/all-job-status-response.dto';
import { GetJobDto } from './dto/get-job.dto'; import { GetJobDto } from './dto/get-job.dto';
import { JobStatusResponseDto } from './response-dto/job-status-response.dto'; import { JobStatusResponseDto } from './response-dto/job-status-response.dto';
import { JobCommandDto } from './dto/job-command.dto'; import { JobCommandDto } from './dto/job-command.dto';
@UseGuards(JwtAuthGuard) @Authenticated({ admin: true })
@UseGuards(AdminRolesGuard)
@ApiTags('Job') @ApiTags('Job')
@ApiBearerAuth() @ApiBearerAuth()
@Controller('jobs') @Controller('jobs')

View file

@ -1,4 +1,4 @@
import { Controller, Get, UseGuards } from '@nestjs/common'; import { Controller, Get } from '@nestjs/common';
import { ServerInfoService } from './server-info.service'; import { ServerInfoService } from './server-info.service';
import { serverVersion } from '../../constants/server_version.constant'; import { serverVersion } from '../../constants/server_version.constant';
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
@ -6,8 +6,7 @@ import { ServerPingResponse } from './response-dto/server-ping-response.dto';
import { ServerVersionReponseDto } from './response-dto/server-version-response.dto'; import { ServerVersionReponseDto } from './response-dto/server-version-response.dto';
import { ServerInfoResponseDto } from './response-dto/server-info-response.dto'; import { ServerInfoResponseDto } from './response-dto/server-info-response.dto';
import { ServerStatsResponseDto } from './response-dto/server-stats-response.dto'; import { ServerStatsResponseDto } from './response-dto/server-stats-response.dto';
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard'; import { Authenticated } from '../../decorators/authenticated.decorator';
import { AdminRolesGuard } from '../../middlewares/admin-role-guard.middleware';
@ApiTags('Server Info') @ApiTags('Server Info')
@Controller('server-info') @Controller('server-info')
@ -29,8 +28,7 @@ export class ServerInfoController {
return serverVersion; return serverVersion;
} }
@UseGuards(JwtAuthGuard) @Authenticated({ admin: true })
@UseGuards(AdminRolesGuard)
@Get('/stats') @Get('/stats')
async getStats(): Promise<ServerStatsResponseDto> { async getStats(): Promise<ServerStatsResponseDto> {
return await this.serverInfoService.getStats(); return await this.serverInfoService.getStats();

View file

@ -4,7 +4,6 @@ import {
Post, Post,
Body, Body,
Param, Param,
UseGuards,
ValidationPipe, ValidationPipe,
Put, Put,
Query, Query,
@ -14,10 +13,9 @@ import {
ParseBoolPipe, ParseBoolPipe,
} from '@nestjs/common'; } from '@nestjs/common';
import { UserService } from './user.service'; import { UserService } from './user.service';
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard'; import { Authenticated } from '../../decorators/authenticated.decorator';
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
import { CreateUserDto } from './dto/create-user.dto'; import { CreateUserDto } from './dto/create-user.dto';
import { AdminRolesGuard } from '../../middlewares/admin-role-guard.middleware';
import { UpdateUserDto } from './dto/update-user.dto'; import { UpdateUserDto } from './dto/update-user.dto';
import { FileInterceptor } from '@nestjs/platform-express'; import { FileInterceptor } from '@nestjs/platform-express';
import { profileImageUploadOption } from '../../config/profile-image-upload.config'; import { profileImageUploadOption } from '../../config/profile-image-upload.config';
@ -33,7 +31,7 @@ import { CreateProfileImageResponseDto } from './response-dto/create-profile-ima
export class UserController { export class UserController {
constructor(private readonly userService: UserService) {} constructor(private readonly userService: UserService) {}
@UseGuards(JwtAuthGuard) @Authenticated()
@ApiBearerAuth() @ApiBearerAuth()
@Get() @Get()
async getAllUsers( async getAllUsers(
@ -48,16 +46,15 @@ export class UserController {
return await this.userService.getUserById(userId); return await this.userService.getUserById(userId);
} }
@UseGuards(JwtAuthGuard) @Authenticated()
@ApiBearerAuth() @ApiBearerAuth()
@Get('me') @Get('me')
async getMyUserInfo(@GetAuthUser() authUser: AuthUserDto): Promise<UserResponseDto> { async getMyUserInfo(@GetAuthUser() authUser: AuthUserDto): Promise<UserResponseDto> {
return await this.userService.getUserInfo(authUser); return await this.userService.getUserInfo(authUser);
} }
@UseGuards(JwtAuthGuard) @Authenticated({ admin: true })
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(AdminRolesGuard)
@Post() @Post()
async createUser( async createUser(
@Body(new ValidationPipe({ transform: true })) createUserDto: CreateUserDto, @Body(new ValidationPipe({ transform: true })) createUserDto: CreateUserDto,
@ -70,7 +67,7 @@ export class UserController {
return await this.userService.getUserCount(); return await this.userService.getUserCount();
} }
@UseGuards(JwtAuthGuard) @Authenticated()
@ApiBearerAuth() @ApiBearerAuth()
@Put() @Put()
async updateUser( async updateUser(
@ -81,7 +78,7 @@ export class UserController {
} }
@UseInterceptors(FileInterceptor('file', profileImageUploadOption)) @UseInterceptors(FileInterceptor('file', profileImageUploadOption))
@UseGuards(JwtAuthGuard) @Authenticated()
@ApiBearerAuth() @ApiBearerAuth()
@ApiConsumes('multipart/form-data') @ApiConsumes('multipart/form-data')
@ApiBody({ @ApiBody({

View file

@ -0,0 +1,16 @@
import { UseGuards } from '@nestjs/common';
import { AdminRolesGuard } from '../middlewares/admin-role-guard.middleware';
import { JwtAuthGuard } from '../modules/immich-jwt/guards/jwt-auth.guard';
interface AuthenticatedOptions {
admin?: boolean;
}
export const Authenticated = (options?: AuthenticatedOptions) => {
const guards: Parameters<typeof UseGuards> = [JwtAuthGuard];
options = options || {};
if (options.admin) {
guards.push(AdminRolesGuard);
}
return UseGuards(...guards);
};

View file

@ -1,42 +1,18 @@
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { CanActivate, ExecutionContext, Injectable, Logger } from '@nestjs/common';
import { Reflector } from '@nestjs/core'; import { Request } from 'express';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserEntity } from '@app/database/entities/user.entity';
import { ImmichJwtService } from '../modules/immich-jwt/immich-jwt.service';
@Injectable() @Injectable()
export class AdminRolesGuard implements CanActivate { export class AdminRolesGuard implements CanActivate {
constructor( logger = new Logger(AdminRolesGuard.name);
private reflector: Reflector,
private jwtService: ImmichJwtService,
@InjectRepository(UserEntity)
private userRepository: Repository<UserEntity>,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> { async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest(); const request = context.switchToHttp().getRequest<Request>();
let accessToken = ''; const isAdmin = request.user?.isAdmin || false;
if (!isAdmin) {
if (request.headers['authorization']) { this.logger.log(`Denied access to admin only route: ${request.path}`);
accessToken = request.headers['authorization'].split(' ')[1];
} else if (request.cookies['immich_access_token']) {
accessToken = request.cookies['immich_access_token'];
} else {
return false; return false;
} }
const { userId } = await this.jwtService.validateToken(accessToken); return true;
if (!userId) {
return false;
}
const user = await this.userRepository.findOne({ where: { id: userId } });
if (!user) {
return false;
}
return user.isAdmin;
} }
} }