mirror of
https://github.com/immich-app/immich.git
synced 2025-01-01 16:41:59 +00:00
feat(server): storage label claim (#3278)
* feat: storage label claim * chore: open api
This commit is contained in:
parent
ed594c1987
commit
f55d63fae8
13 changed files with 49 additions and 5 deletions
6
cli/src/api/open-api/api.ts
generated
6
cli/src/api/open-api/api.ts
generated
|
@ -2596,6 +2596,12 @@ export interface SystemConfigOAuthDto {
|
||||||
* @memberof SystemConfigOAuthDto
|
* @memberof SystemConfigOAuthDto
|
||||||
*/
|
*/
|
||||||
'scope': string;
|
'scope': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof SystemConfigOAuthDto
|
||||||
|
*/
|
||||||
|
'storageLabelClaim': string;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
|
BIN
mobile/openapi/doc/SystemConfigOAuthDto.md
generated
BIN
mobile/openapi/doc/SystemConfigOAuthDto.md
generated
Binary file not shown.
BIN
mobile/openapi/lib/model/system_config_o_auth_dto.dart
generated
BIN
mobile/openapi/lib/model/system_config_o_auth_dto.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/system_config_o_auth_dto_test.dart
generated
BIN
mobile/openapi/test/system_config_o_auth_dto_test.dart
generated
Binary file not shown.
|
@ -6503,6 +6503,9 @@
|
||||||
"scope": {
|
"scope": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"storageLabelClaim": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"buttonText": {
|
"buttonText": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -6525,6 +6528,7 @@
|
||||||
"clientId",
|
"clientId",
|
||||||
"clientSecret",
|
"clientSecret",
|
||||||
"scope",
|
"scope",
|
||||||
|
"storageLabelClaim",
|
||||||
"buttonText",
|
"buttonText",
|
||||||
"autoRegister",
|
"autoRegister",
|
||||||
"autoLaunch",
|
"autoLaunch",
|
||||||
|
|
|
@ -240,11 +240,19 @@ export class AuthService {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.log(`Registering new user: ${profile.email}/${profile.sub}`);
|
this.logger.log(`Registering new user: ${profile.email}/${profile.sub}`);
|
||||||
|
this.logger.verbose(`OAuth Profile: ${JSON.stringify(profile)}`);
|
||||||
|
|
||||||
|
let storageLabel: string | null = profile[config.oauth.storageLabelClaim as keyof OAuthProfile] as string;
|
||||||
|
if (typeof storageLabel !== 'string') {
|
||||||
|
storageLabel = null;
|
||||||
|
}
|
||||||
|
|
||||||
user = await this.userCore.createUser({
|
user = await this.userCore.createUser({
|
||||||
firstName: profile.given_name || '',
|
firstName: profile.given_name || '',
|
||||||
lastName: profile.family_name || '',
|
lastName: profile.family_name || '',
|
||||||
email: profile.email,
|
email: profile.email,
|
||||||
oauthId: profile.sub,
|
oauthId: profile.sub,
|
||||||
|
storageLabel,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,9 @@ export class SystemConfigOAuthDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
scope!: string;
|
scope!: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
storageLabelClaim!: string;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
buttonText!: string;
|
buttonText!: string;
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ export const defaults = Object.freeze<SystemConfig>({
|
||||||
mobileOverrideEnabled: false,
|
mobileOverrideEnabled: false,
|
||||||
mobileRedirectUri: '',
|
mobileRedirectUri: '',
|
||||||
scope: 'openid email profile',
|
scope: 'openid email profile',
|
||||||
|
storageLabelClaim: 'preferred_username',
|
||||||
buttonText: 'Login with OAuth',
|
buttonText: 'Login with OAuth',
|
||||||
autoRegister: true,
|
autoRegister: true,
|
||||||
autoLaunch: false,
|
autoLaunch: false,
|
||||||
|
|
|
@ -53,6 +53,7 @@ const updatedConfig = Object.freeze<SystemConfig>({
|
||||||
mobileOverrideEnabled: false,
|
mobileOverrideEnabled: false,
|
||||||
mobileRedirectUri: '',
|
mobileRedirectUri: '',
|
||||||
scope: 'openid email profile',
|
scope: 'openid email profile',
|
||||||
|
storageLabelClaim: 'preferred_username',
|
||||||
},
|
},
|
||||||
passwordLogin: {
|
passwordLogin: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|
|
@ -8,9 +8,9 @@ import {
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { constants, createReadStream, ReadStream } from 'fs';
|
import { constants, createReadStream, ReadStream } from 'fs';
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
|
import sanitize from 'sanitize-filename';
|
||||||
import { AuthUserDto } from '../auth';
|
import { AuthUserDto } from '../auth';
|
||||||
import { ICryptoRepository } from '../crypto';
|
import { ICryptoRepository } from '../crypto';
|
||||||
import { CreateAdminDto, CreateUserDto, CreateUserOAuthDto } from './dto/create-user.dto';
|
|
||||||
import { IUserRepository, UserListFilter } from './user.repository';
|
import { IUserRepository, UserListFilter } from './user.repository';
|
||||||
|
|
||||||
const SALT_ROUNDS = 10;
|
const SALT_ROUNDS = 10;
|
||||||
|
@ -67,13 +67,13 @@ export class UserCore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createUser(createUserDto: CreateUserDto | CreateAdminDto | CreateUserOAuthDto): Promise<UserEntity> {
|
async createUser(dto: Partial<UserEntity> & { email: string }): Promise<UserEntity> {
|
||||||
const user = await this.userRepository.getByEmail(createUserDto.email);
|
const user = await this.userRepository.getByEmail(dto.email);
|
||||||
if (user) {
|
if (user) {
|
||||||
throw new BadRequestException('User exists');
|
throw new BadRequestException('User exists');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(createUserDto as CreateAdminDto).isAdmin) {
|
if (!dto.isAdmin) {
|
||||||
const localAdmin = await this.userRepository.getAdmin();
|
const localAdmin = await this.userRepository.getAdmin();
|
||||||
if (!localAdmin) {
|
if (!localAdmin) {
|
||||||
throw new BadRequestException('The first registered account must the administrator.');
|
throw new BadRequestException('The first registered account must the administrator.');
|
||||||
|
@ -81,10 +81,13 @@ export class UserCore {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload: Partial<UserEntity> = { ...createUserDto };
|
const payload: Partial<UserEntity> = { ...dto };
|
||||||
if (payload.password) {
|
if (payload.password) {
|
||||||
payload.password = await this.cryptoRepository.hashBcrypt(payload.password, SALT_ROUNDS);
|
payload.password = await this.cryptoRepository.hashBcrypt(payload.password, SALT_ROUNDS);
|
||||||
}
|
}
|
||||||
|
if (payload.storageLabel) {
|
||||||
|
payload.storageLabel = sanitize(payload.storageLabel);
|
||||||
|
}
|
||||||
return this.userRepository.create(payload);
|
return this.userRepository.create(payload);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.error(e, 'Create new user');
|
Logger.error(e, 'Create new user');
|
||||||
|
|
|
@ -40,6 +40,7 @@ export enum SystemConfigKey {
|
||||||
OAUTH_CLIENT_ID = 'oauth.clientId',
|
OAUTH_CLIENT_ID = 'oauth.clientId',
|
||||||
OAUTH_CLIENT_SECRET = 'oauth.clientSecret',
|
OAUTH_CLIENT_SECRET = 'oauth.clientSecret',
|
||||||
OAUTH_SCOPE = 'oauth.scope',
|
OAUTH_SCOPE = 'oauth.scope',
|
||||||
|
OAUTH_STORAGE_LABEL_CLAIM = 'oauth.storageLabelClaim',
|
||||||
OAUTH_AUTO_LAUNCH = 'oauth.autoLaunch',
|
OAUTH_AUTO_LAUNCH = 'oauth.autoLaunch',
|
||||||
OAUTH_BUTTON_TEXT = 'oauth.buttonText',
|
OAUTH_BUTTON_TEXT = 'oauth.buttonText',
|
||||||
OAUTH_AUTO_REGISTER = 'oauth.autoRegister',
|
OAUTH_AUTO_REGISTER = 'oauth.autoRegister',
|
||||||
|
@ -89,6 +90,7 @@ export interface SystemConfig {
|
||||||
clientId: string;
|
clientId: string;
|
||||||
clientSecret: string;
|
clientSecret: string;
|
||||||
scope: string;
|
scope: string;
|
||||||
|
storageLabelClaim: string;
|
||||||
buttonText: string;
|
buttonText: string;
|
||||||
autoRegister: boolean;
|
autoRegister: boolean;
|
||||||
autoLaunch: boolean;
|
autoLaunch: boolean;
|
||||||
|
|
6
web/src/api/open-api/api.ts
generated
6
web/src/api/open-api/api.ts
generated
|
@ -2596,6 +2596,12 @@ export interface SystemConfigOAuthDto {
|
||||||
* @memberof SystemConfigOAuthDto
|
* @memberof SystemConfigOAuthDto
|
||||||
*/
|
*/
|
||||||
'scope': string;
|
'scope': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof SystemConfigOAuthDto
|
||||||
|
*/
|
||||||
|
'storageLabelClaim': string;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
|
|
@ -155,6 +155,16 @@
|
||||||
isEdited={!(oauthConfig.scope == savedConfig.scope)}
|
isEdited={!(oauthConfig.scope == savedConfig.scope)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SettingInputField
|
||||||
|
inputType={SettingInputFieldType.TEXT}
|
||||||
|
label="STORAGE LABEL CLAIM"
|
||||||
|
desc="Automatically set the user's storage label to the value of this claim."
|
||||||
|
bind:value={oauthConfig.storageLabelClaim}
|
||||||
|
required={true}
|
||||||
|
disabled={!oauthConfig.storageLabelClaim}
|
||||||
|
isEdited={!(oauthConfig.storageLabelClaim == savedConfig.storageLabelClaim)}
|
||||||
|
/>
|
||||||
|
|
||||||
<SettingInputField
|
<SettingInputField
|
||||||
inputType={SettingInputFieldType.TEXT}
|
inputType={SettingInputFieldType.TEXT}
|
||||||
label="BUTTON TEXT"
|
label="BUTTON TEXT"
|
||||||
|
|
Loading…
Reference in a new issue