Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(server): correct user permission to update user info (#716)
- Loading branch information
1 parent
03fc070
commit ece94f6
Showing
3 changed files
with
157 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
137 changes: 137 additions & 0 deletions
137
server/apps/immich/src/api-v1/user/user.service.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import { UserEntity } from '@app/database/entities/user.entity'; | ||
import { BadRequestException, NotFoundException } from '@nestjs/common'; | ||
import { AuthUserDto } from '../../decorators/auth-user.decorator'; | ||
import { IUserRepository } from './user-repository'; | ||
import { UserService } from './user.service'; | ||
|
||
describe('UserService', () => { | ||
let sui: UserService; | ||
let userRepositoryMock: jest.Mocked<IUserRepository>; | ||
|
||
const adminAuthUser: AuthUserDto = Object.freeze({ | ||
id: 'admin_id', | ||
email: 'admin@test.com', | ||
}); | ||
|
||
const immichAuthUser: AuthUserDto = Object.freeze({ | ||
id: 'immich_id', | ||
email: 'immich@test.com', | ||
}); | ||
|
||
const adminUser: UserEntity = Object.freeze({ | ||
id: 'admin_id', | ||
email: 'admin@test.com', | ||
password: 'admin_password', | ||
salt: 'admin_salt', | ||
firstName: 'admin_first_name', | ||
lastName: 'admin_last_name', | ||
isAdmin: true, | ||
shouldChangePassword: false, | ||
profileImagePath: '', | ||
createdAt: '2021-01-01', | ||
}); | ||
|
||
const immichUser: UserEntity = Object.freeze({ | ||
id: 'immich_id', | ||
email: 'immich@test.com', | ||
password: 'immich_password', | ||
salt: 'immich_salt', | ||
firstName: 'immich_first_name', | ||
lastName: 'immich_last_name', | ||
isAdmin: false, | ||
shouldChangePassword: false, | ||
profileImagePath: '', | ||
createdAt: '2021-01-01', | ||
}); | ||
|
||
const updatedImmichUser: UserEntity = Object.freeze({ | ||
id: 'immich_id', | ||
email: 'immich@test.com', | ||
password: 'immich_password', | ||
salt: 'immich_salt', | ||
firstName: 'updated_immich_first_name', | ||
lastName: 'updated_immich_last_name', | ||
isAdmin: false, | ||
shouldChangePassword: true, | ||
profileImagePath: '', | ||
createdAt: '2021-01-01', | ||
}); | ||
|
||
beforeAll(() => { | ||
userRepositoryMock = { | ||
create: jest.fn(), | ||
createProfileImage: jest.fn(), | ||
get: jest.fn(), | ||
getByEmail: jest.fn(), | ||
getList: jest.fn(), | ||
update: jest.fn(), | ||
}; | ||
|
||
sui = new UserService(userRepositoryMock); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(sui).toBeDefined(); | ||
}); | ||
|
||
describe('Update user', () => { | ||
it('should update user', () => { | ||
const requestor = immichAuthUser; | ||
const userToUpdate = immichUser; | ||
|
||
userRepositoryMock.get.mockImplementationOnce(() => Promise.resolve(immichUser)); | ||
userRepositoryMock.get.mockImplementationOnce(() => Promise.resolve(userToUpdate)); | ||
userRepositoryMock.update.mockImplementationOnce(() => Promise.resolve(updatedImmichUser)); | ||
|
||
const result = sui.updateUser(requestor, { | ||
id: userToUpdate.id, | ||
shouldChangePassword: true, | ||
}); | ||
expect(result).resolves.toBeDefined(); | ||
}); | ||
|
||
it('user can only update its information', () => { | ||
const requestor = immichAuthUser; | ||
|
||
userRepositoryMock.get.mockImplementationOnce(() => Promise.resolve(immichUser)); | ||
|
||
const result = sui.updateUser(requestor, { | ||
id: 'not_immich_auth_user_id', | ||
password: 'I take over your account now', | ||
}); | ||
expect(result).rejects.toBeInstanceOf(BadRequestException); | ||
}); | ||
|
||
it('admin can update any user information', async () => { | ||
const requestor = adminAuthUser; | ||
const userToUpdate = immichUser; | ||
|
||
userRepositoryMock.get.mockImplementationOnce(() => Promise.resolve(adminUser)); | ||
userRepositoryMock.get.mockImplementationOnce(() => Promise.resolve(userToUpdate)); | ||
userRepositoryMock.update.mockImplementationOnce(() => Promise.resolve(updatedImmichUser)); | ||
|
||
const result = await sui.updateUser(requestor, { | ||
id: userToUpdate.id, | ||
shouldChangePassword: true, | ||
}); | ||
|
||
expect(result).toBeDefined(); | ||
expect(result.id).toEqual(updatedImmichUser.id); | ||
expect(result.shouldChangePassword).toEqual(updatedImmichUser.shouldChangePassword); | ||
}); | ||
|
||
it('update user information should throw error if user not found', () => { | ||
const requestor = adminAuthUser; | ||
const userToUpdate = immichUser; | ||
|
||
userRepositoryMock.get.mockImplementationOnce(() => Promise.resolve(adminUser)); | ||
userRepositoryMock.get.mockImplementationOnce(() => Promise.resolve(null)); | ||
|
||
const result = sui.updateUser(requestor, { | ||
id: userToUpdate.id, | ||
shouldChangePassword: true, | ||
}); | ||
expect(result).rejects.toBeInstanceOf(NotFoundException); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters