mirror of
https://github.com/immich-app/immich.git
synced 2025-01-07 00:50:23 -05:00
feat(server): Allow activating non-admin user with server license (#11206)
* feat(server): allow server license to activate a user * feat(web): send server+client licenses to user activation when non-admin * chore(server): update test to allow server license to activate user * fix(web): correctly load user to determine where to save license
This commit is contained in:
parent
d180373ec1
commit
ade2901259
3 changed files with 29 additions and 7 deletions
|
@ -274,7 +274,7 @@ describe(UserService.name, () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setLicense', () => {
|
describe('setLicense', () => {
|
||||||
it('should save license if valid', async () => {
|
it('should save client license if valid', async () => {
|
||||||
userMock.upsertMetadata.mockResolvedValue();
|
userMock.upsertMetadata.mockResolvedValue();
|
||||||
|
|
||||||
const license = { licenseKey: 'IMCL-license-key', activationKey: 'activation-key' };
|
const license = { licenseKey: 'IMCL-license-key', activationKey: 'activation-key' };
|
||||||
|
@ -286,6 +286,18 @@ describe(UserService.name, () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should save server license as client if valid', async () => {
|
||||||
|
userMock.upsertMetadata.mockResolvedValue();
|
||||||
|
|
||||||
|
const license = { licenseKey: 'IMSV-license-key', activationKey: 'activation-key' };
|
||||||
|
await sut.setLicense(authStub.user1, license);
|
||||||
|
|
||||||
|
expect(userMock.upsertMetadata).toHaveBeenCalledWith(authStub.user1.user.id, {
|
||||||
|
key: UserMetadataKey.LICENSE,
|
||||||
|
value: expect.any(Object),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should not save license if invalid', async () => {
|
it('should not save license if invalid', async () => {
|
||||||
userMock.upsertMetadata.mockResolvedValue();
|
userMock.upsertMetadata.mockResolvedValue();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { getClientLicensePublicKey } from 'src/config';
|
import { getClientLicensePublicKey, getServerLicensePublicKey } from 'src/config';
|
||||||
import { SALT_ROUNDS } from 'src/constants';
|
import { SALT_ROUNDS } from 'src/constants';
|
||||||
import { StorageCore, StorageFolder } from 'src/cores/storage.core';
|
import { StorageCore, StorageFolder } from 'src/cores/storage.core';
|
||||||
import { SystemConfigCore } from 'src/cores/system-config.core';
|
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||||
|
@ -138,16 +138,23 @@ export class UserService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async setLicense(auth: AuthDto, license: LicenseKeyDto): Promise<LicenseResponseDto> {
|
async setLicense(auth: AuthDto, license: LicenseKeyDto): Promise<LicenseResponseDto> {
|
||||||
if (!license.licenseKey.startsWith('IMCL-')) {
|
if (!license.licenseKey.startsWith('IMCL-') && !license.licenseKey.startsWith('IMSV-')) {
|
||||||
throw new BadRequestException('Invalid license key');
|
throw new BadRequestException('Invalid license key');
|
||||||
}
|
}
|
||||||
const licenseValid = this.cryptoRepository.verifySha256(
|
|
||||||
|
const clientLicenseValid = this.cryptoRepository.verifySha256(
|
||||||
license.licenseKey,
|
license.licenseKey,
|
||||||
license.activationKey,
|
license.activationKey,
|
||||||
getClientLicensePublicKey(),
|
getClientLicensePublicKey(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!licenseValid) {
|
const serverLicenseValid = this.cryptoRepository.verifySha256(
|
||||||
|
license.licenseKey,
|
||||||
|
license.activationKey,
|
||||||
|
getServerLicensePublicKey(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!clientLicenseValid && !serverLicenseValid) {
|
||||||
throw new BadRequestException('Invalid license key');
|
throw new BadRequestException('Invalid license key');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,14 @@ import type { ImmichLicense } from '$lib/constants';
|
||||||
import { serverConfig } from '$lib/stores/server-config.store';
|
import { serverConfig } from '$lib/stores/server-config.store';
|
||||||
import { setServerLicense, setUserLicense, type LicenseResponseDto } from '@immich/sdk';
|
import { setServerLicense, setUserLicense, type LicenseResponseDto } from '@immich/sdk';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
|
import { loadUser } from './auth';
|
||||||
|
|
||||||
export const activateLicense = async (licenseKey: string, activationKey: string): Promise<LicenseResponseDto> => {
|
export const activateLicense = async (licenseKey: string, activationKey: string): Promise<LicenseResponseDto> => {
|
||||||
const isServerKey = licenseKey.search('IMSV') !== -1;
|
// Send server key to user activation if user is not admin
|
||||||
|
const user = await loadUser();
|
||||||
|
const isServerActivation = user?.isAdmin && licenseKey.search('IMSV') !== -1;
|
||||||
const licenseKeyDto = { licenseKey, activationKey };
|
const licenseKeyDto = { licenseKey, activationKey };
|
||||||
return isServerKey ? setServerLicense({ licenseKeyDto }) : setUserLicense({ licenseKeyDto });
|
return isServerActivation ? setServerLicense({ licenseKeyDto }) : setUserLicense({ licenseKeyDto });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getActivationKey = async (licenseKey: string): Promise<string> => {
|
export const getActivationKey = async (licenseKey: string): Promise<string> => {
|
||||||
|
|
Loading…
Reference in a new issue