mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
feat(server): Add publicUsers toggle for user search (#14330)
* feat(server): Add publicUsers toggle for user search * tests * docs: add check:typescript for web PR checklist * return auth.user when publicUsers is false - app testing --------- Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
parent
b6ec79cbdd
commit
5417e34fb6
19 changed files with 93 additions and 11 deletions
|
|
@ -149,6 +149,7 @@ export interface SystemConfig {
|
|||
server: {
|
||||
externalDomain: string;
|
||||
loginPageMessage: string;
|
||||
publicUsers: boolean;
|
||||
};
|
||||
user: {
|
||||
deleteDelay: number;
|
||||
|
|
@ -296,6 +297,7 @@ export const defaults = Object.freeze<SystemConfig>({
|
|||
server: {
|
||||
externalDomain: '',
|
||||
loginPageMessage: '',
|
||||
publicUsers: true,
|
||||
},
|
||||
notifications: {
|
||||
smtp: {
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ export class UserController {
|
|||
|
||||
@Get()
|
||||
@Authenticated()
|
||||
searchUsers(): Promise<UserResponseDto[]> {
|
||||
return this.service.search();
|
||||
searchUsers(@Auth() auth: AuthDto): Promise<UserResponseDto[]> {
|
||||
return this.service.search(auth);
|
||||
}
|
||||
|
||||
@Get('me')
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ export class ServerConfigDto {
|
|||
isInitialized!: boolean;
|
||||
isOnboarded!: boolean;
|
||||
externalDomain!: string;
|
||||
publicUsers!: boolean;
|
||||
mapDarkStyleUrl!: string;
|
||||
mapLightStyleUrl!: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -404,6 +404,9 @@ class SystemConfigServerDto {
|
|||
|
||||
@IsString()
|
||||
loginPageMessage!: string;
|
||||
|
||||
@IsBoolean()
|
||||
publicUsers!: boolean;
|
||||
}
|
||||
|
||||
class SystemConfigSmtpTransportDto {
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@ describe(ServerService.name, () => {
|
|||
isInitialized: undefined,
|
||||
isOnboarded: false,
|
||||
externalDomain: '',
|
||||
publicUsers: true,
|
||||
mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json',
|
||||
mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ export class ServerService extends BaseService {
|
|||
isInitialized,
|
||||
isOnboarded: onboarding?.isOnboarded || false,
|
||||
externalDomain: config.server.externalDomain,
|
||||
publicUsers: config.server.publicUsers,
|
||||
mapDarkStyleUrl: config.map.darkStyle,
|
||||
mapLightStyleUrl: config.map.lightStyle,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ const updatedConfig = Object.freeze<SystemConfig>({
|
|||
server: {
|
||||
externalDomain: '',
|
||||
loginPageMessage: '',
|
||||
publicUsers: true,
|
||||
},
|
||||
storageTemplate: {
|
||||
enabled: false,
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ describe(UserService.name, () => {
|
|||
});
|
||||
|
||||
describe('getAll', () => {
|
||||
it('should get all users', async () => {
|
||||
it('admin should get all users', async () => {
|
||||
userMock.getList.mockResolvedValue([userStub.admin]);
|
||||
await expect(sut.search()).resolves.toEqual([
|
||||
await expect(sut.search(authStub.admin)).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
id: authStub.admin.user.id,
|
||||
email: authStub.admin.user.email,
|
||||
|
|
@ -48,6 +48,29 @@ describe(UserService.name, () => {
|
|||
]);
|
||||
expect(userMock.getList).toHaveBeenCalledWith({ withDeleted: false });
|
||||
});
|
||||
|
||||
it('non-admin should get all users when publicUsers enabled', async () => {
|
||||
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||
await expect(sut.search(authStub.user1)).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
id: authStub.user1.user.id,
|
||||
email: authStub.user1.user.email,
|
||||
}),
|
||||
]);
|
||||
expect(userMock.getList).toHaveBeenCalledWith({ withDeleted: false });
|
||||
});
|
||||
|
||||
it('non-admin user should only receive itself when publicUsers is disabled', async () => {
|
||||
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||
systemMock.get.mockResolvedValue(systemConfigStub.publicUsersDisabled);
|
||||
await expect(sut.search(authStub.user1)).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
id: authStub.user1.user.id,
|
||||
email: authStub.user1.user.email,
|
||||
}),
|
||||
]);
|
||||
expect(userMock.getList).not.toHaveBeenCalledWith({ withDeleted: false });
|
||||
});
|
||||
});
|
||||
|
||||
describe('get', () => {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,14 @@ import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/uti
|
|||
|
||||
@Injectable()
|
||||
export class UserService extends BaseService {
|
||||
async search(): Promise<UserResponseDto[]> {
|
||||
const users = await this.userRepository.getList({ withDeleted: false });
|
||||
async search(auth: AuthDto): Promise<UserResponseDto[]> {
|
||||
const config = await this.getConfig({ withCache: false });
|
||||
|
||||
let users: UserEntity[] = [auth.user];
|
||||
if (auth.user.isAdmin || config.server.publicUsers) {
|
||||
users = await this.userRepository.getList({ withDeleted: false });
|
||||
}
|
||||
|
||||
return users.map((user) => mapUser(user));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue