Skip to content

Commit

Permalink
CB-5123. Added backend part, added new parameter licenseStatus to ser… (
Browse files Browse the repository at this point in the history
#2831)

* CB-5123. Added backend part, added new parameter licenseStatus to server config gql

* CB-5123 feat: display license status information

* CB-5123 fix: after review

* CB-5123. Added version

---------

Co-authored-by: Aleksei Potsetsuev <[email protected]>
Co-authored-by: Evgenia Bezborodova <[email protected]>
  • Loading branch information
3 people authored Aug 13, 2024
1 parent 2d45b29 commit 9254684
Show file tree
Hide file tree
Showing 37 changed files with 487 additions and 212 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ type ServerConfig {

licenseRequired: Boolean!
licenseValid: Boolean!
licenseStatus: String @since(version: "24.1.5")

sessionExpireTime: Int!
localHostAddress: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ public boolean isLicenseValid() {
return application.isLicenseValid();
}

@Property
public String getLicenseStatus() {
return application.getLicenseStatus();
}

@Property
public boolean isConfigurationMode() {
return application.isConfigurationMode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class AdministrationScreenService {
}

get publicDisabled(): boolean {
return this.serverConfigResource.publicDisabled;
return this.permissionsService.publicDisabled;
}

readonly ensurePermissions: IExecutor;
Expand Down
15 changes: 9 additions & 6 deletions webapp/packages/core-authentication/src/PasswordPolicyService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { computed, makeObservable } from 'mobx';

import { injectable } from '@cloudbeaver/core-di';
import { LocalizationService } from '@cloudbeaver/core-localization';
import { ServerConfigResource } from '@cloudbeaver/core-root';
import { PasswordPolicyResource } from '@cloudbeaver/core-root';
import type { PasswordPolicyConfig } from '@cloudbeaver/core-sdk';

const DEFAULT_PASSWORD_POLICY: PasswordPolicyConfig = {
Expand All @@ -25,22 +25,25 @@ type ValidationResult = { isValid: true; errorMessage: null } | { isValid: false
export class PasswordPolicyService {
get config(): PasswordPolicyConfig {
return {
minLength: this.serverConfigResource.data?.passwordPolicyConfiguration?.minLength || DEFAULT_PASSWORD_POLICY.minLength,
minNumberCount: this.serverConfigResource.data?.passwordPolicyConfiguration?.minNumberCount || DEFAULT_PASSWORD_POLICY.minNumberCount,
minSymbolCount: this.serverConfigResource.data?.passwordPolicyConfiguration?.minSymbolCount || DEFAULT_PASSWORD_POLICY.minSymbolCount,
requireMixedCase: this.serverConfigResource.data?.passwordPolicyConfiguration?.requireMixedCase || DEFAULT_PASSWORD_POLICY.requireMixedCase,
minLength: this.passwordPolicyResource.data?.minLength || DEFAULT_PASSWORD_POLICY.minLength,
minNumberCount: this.passwordPolicyResource.data?.minNumberCount || DEFAULT_PASSWORD_POLICY.minNumberCount,
minSymbolCount: this.passwordPolicyResource.data?.minSymbolCount || DEFAULT_PASSWORD_POLICY.minSymbolCount,
requireMixedCase: this.passwordPolicyResource.data?.requireMixedCase || DEFAULT_PASSWORD_POLICY.requireMixedCase,
};
}

constructor(
private readonly serverConfigResource: ServerConfigResource,
private readonly passwordPolicyResource: PasswordPolicyResource,
private readonly localizationService: LocalizationService,
) {
makeObservable(this, {
config: computed,
});
}

/**
* PasswordPolicyResource should be loaded before calling this method
*/
validatePassword(password: string): ValidationResult {
const trimmedPassword = password.trim();

Expand Down
3 changes: 3 additions & 0 deletions webapp/packages/core-blocks/src/usePasswordValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
*/
import { PasswordPolicyService } from '@cloudbeaver/core-authentication';
import { useService } from '@cloudbeaver/core-di';
import { PasswordPolicyResource } from '@cloudbeaver/core-root';

import { useCustomInputValidation } from './FormControls/useCustomInputValidation';
import { useResource } from './ResourcesHooks/useResource';

export function usePasswordValidation() {
useResource(usePasswordValidation, PasswordPolicyResource, undefined);
const passwordPolicyService = useService(PasswordPolicyService);

const ref = useCustomInputValidation<string>(value => {
Expand Down
53 changes: 34 additions & 19 deletions webapp/packages/core-events/src/NotificationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,26 +80,41 @@ export class NotificationService {

const id = this.notificationNextId++;

const notification: INotification<TProps> = {
id,
uuid: options.uuid,
title: options.title,
message: options.message,
details: options.details,
isSilent: !!options.isSilent,
customComponent: options.customComponent,
extraProps: options.extraProps || ({} as TProps),
autoClose: options.autoClose,
persistent: options.persistent,
state: observable({ deleteDelay: 0 }),
timestamp: options.timestamp || Date.now(),
type,
close: delayDeleting => {
this.close(id, delayDeleting);
options.onClose?.(delayDeleting);
const notification: INotification<TProps> = observable(
{
id,
uuid: options.uuid,
title: options.title,
message: options.message,
details: options.details,
isSilent: !!options.isSilent,
customComponent: options.customComponent,
extraProps: options.extraProps || ({} as TProps),
autoClose: options.autoClose,
persistent: options.persistent,
state: observable({ deleteDelay: 0 }),
timestamp: options.timestamp || Date.now(),
type,
close: delayDeleting => {
this.close(id, delayDeleting);
options.onClose?.(delayDeleting);
},
showDetails: this.showDetails.bind(this, id),
},
{
title: observable.ref,
message: observable.ref,
details: observable.ref,
persistent: observable.ref,
isSilent: observable.ref,
customComponent: observable.ref,
extraProps: observable.ref,
autoClose: observable.ref,
type: observable.ref,
timestamp: observable.ref,
showDetails: observable.ref,
},
showDetails: this.showDetails.bind(this, id),
};
);

this.notificationList.addValue(notification);

Expand Down
6 changes: 3 additions & 3 deletions webapp/packages/core-projects/src/ProjectInfoResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { AppAuthService, UserInfoResource } from '@cloudbeaver/core-authentication';
import { injectable } from '@cloudbeaver/core-di';
import { CachedMapAllKey, CachedMapResource, resourceKeyList } from '@cloudbeaver/core-resource';
import { ServerConfigResource } from '@cloudbeaver/core-root';
import { PermissionsService } from '@cloudbeaver/core-root';
import { GraphQLService, RmResourceType, ProjectInfo as SchemaProjectInfo } from '@cloudbeaver/core-sdk';

import { createResourceOfType } from './createResourceOfType';
Expand All @@ -21,7 +21,7 @@ export class ProjectInfoResource extends CachedMapResource<string, ProjectInfo>
constructor(
private readonly graphQLService: GraphQLService,
private readonly userInfoResource: UserInfoResource,
serverConfigResource: ServerConfigResource,
permissionsService: PermissionsService,
appAuthService: AppAuthService,
) {
super(() => new Map(), []);
Expand All @@ -32,7 +32,7 @@ export class ProjectInfoResource extends CachedMapResource<string, ProjectInfo>
() => CachedMapAllKey,
);
appAuthService.requireAuthentication(this);
serverConfigResource.requirePublic(this);
permissionsService.requirePublic(this);
this.userInfoResource.onUserChange.addPostHandler(() => {
this.clear();
});
Expand Down
103 changes: 103 additions & 0 deletions webapp/packages/core-root/src/DefaultNavigatorSettingsResource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { action, makeObservable, observable } from 'mobx';

import { injectable } from '@cloudbeaver/core-di';
import { CachedDataResource } from '@cloudbeaver/core-resource';
import { DefaultNavigatorSettingsFragment, GraphQLService, NavigatorSettingsInput } from '@cloudbeaver/core-sdk';

import { isNavigatorViewSettingsEqual } from './ConnectionNavigatorViewSettings';
import { ServerConfigResource } from './ServerConfigResource';

export type DefaultNavigatorSettings = DefaultNavigatorSettingsFragment['defaultNavigatorSettings'];

@injectable()
export class DefaultNavigatorSettingsResource extends CachedDataResource<DefaultNavigatorSettings | null> {
update: NavigatorSettingsInput;

constructor(
private readonly graphQLService: GraphQLService,
serverConfigResource: ServerConfigResource,
) {
super(() => null, undefined, []);
this.sync(serverConfigResource);

this.update = getDefaultNavigatorSettings();

makeObservable<this, 'syncUpdateData'>(this, {
update: observable,
unlinkUpdate: action,
syncUpdateData: action,
});
}

isChanged(): boolean {
if (!this.data) {
return false;
}

return !isNavigatorViewSettingsEqual(this.data, this.update);
}

setDataUpdate(update: NavigatorSettingsInput): void {
this.update = update;
}

resetUpdate(): void {
if (this.data) {
this.syncUpdateData(this.data);
}
}

unlinkUpdate(): void {
if (this.data) {
Object.assign(this.update, this.data);
} else {
this.update = getDefaultNavigatorSettings();
}
}

async save(): Promise<void> {
await this.performUpdate(
undefined,
undefined,
async () => {
await this.graphQLService.sdk.setDefaultNavigatorSettings({ settings: this.update });

this.setData(await this.loader());

this.onDataOutdated.execute();
},
() => !this.isChanged(),
);
}

protected async loader(): Promise<DefaultNavigatorSettings> {
const { defaultNavigatorSettings } = await this.graphQLService.sdk.getDefaultNavigatorSettings();

this.syncUpdateData(defaultNavigatorSettings.defaultNavigatorSettings);

return defaultNavigatorSettings.defaultNavigatorSettings;
}

private syncUpdateData(defaultNavigatorSettings: DefaultNavigatorSettings) {
Object.assign(this.update, defaultNavigatorSettings);
}
}

function getDefaultNavigatorSettings(): NavigatorSettingsInput {
return {
hideFolders: false,
hideSchemas: false,
hideVirtualModel: false,
mergeEntities: false,
showOnlyEntities: false,
showSystemObjects: false,
showUtilityObjects: false,
};
}
31 changes: 31 additions & 0 deletions webapp/packages/core-root/src/PasswordPolicyResource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { injectable } from '@cloudbeaver/core-di';
import { CachedDataResource } from '@cloudbeaver/core-resource';
import { GraphQLService, PasswordPolicyFragment } from '@cloudbeaver/core-sdk';

import { ServerConfigResource } from './ServerConfigResource';

export type PasswordPolicy = PasswordPolicyFragment['passwordPolicyConfiguration'];

@injectable()
export class PasswordPolicyResource extends CachedDataResource<PasswordPolicy | null> {
constructor(
private readonly graphQLService: GraphQLService,
serverConfigResource: ServerConfigResource,
) {
super(() => null, undefined, []);
this.sync(serverConfigResource);
}

protected async loader(): Promise<PasswordPolicy> {
const { passwordPolicy } = await this.graphQLService.sdk.getPasswordPolicy();

return passwordPolicy.passwordPolicyConfiguration;
}
}
25 changes: 24 additions & 1 deletion webapp/packages/core-root/src/PermissionsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,37 @@
* you may not use this file except in compliance with the License.
*/
import { injectable } from '@cloudbeaver/core-di';
import { ExecutorInterrupter } from '@cloudbeaver/core-executor';
import { CachedResource } from '@cloudbeaver/core-resource';

import { ServerConfigResource } from './ServerConfigResource';
import { ServerLicenseStatusResource } from './ServerLicenseStatusResource';
import { SessionPermissionsResource } from './SessionPermissionsResource';

export enum EPermission {}

@injectable()
export class PermissionsService {
constructor(private readonly permissions: SessionPermissionsResource) {}
get publicDisabled(): boolean {
return (
this.serverConfigResource.configurationMode ||
(this.serverLicenseStatusResource?.licenseRequired && !this.serverLicenseStatusResource.licenseValid)
);
}

constructor(
private readonly permissions: SessionPermissionsResource,
private readonly serverConfigResource: ServerConfigResource,
private readonly serverLicenseStatusResource: ServerLicenseStatusResource,
) {}

requirePublic<T>(resource: CachedResource<any, any, T, any, any>, map?: (param: void) => T): this {
resource.preloadResource(this.serverLicenseStatusResource, () => {}).before(ExecutorInterrupter.interrupter(() => this.publicDisabled));

this.serverLicenseStatusResource.outdateResource<T>(resource, map as any);

return this;
}

has(id: string): boolean {
return this.permissions.has(id);
Expand Down
31 changes: 31 additions & 0 deletions webapp/packages/core-root/src/ProductInfoResource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { injectable } from '@cloudbeaver/core-di';
import { CachedDataResource } from '@cloudbeaver/core-resource';
import { GraphQLService, ProductInfoFragment } from '@cloudbeaver/core-sdk';

import { ServerConfigResource } from './ServerConfigResource';

export type ProductInfo = ProductInfoFragment['productInfo'];

@injectable()
export class ProductInfoResource extends CachedDataResource<ProductInfo | null> {
constructor(
private readonly graphQLService: GraphQLService,
serverConfigResource: ServerConfigResource,
) {
super(() => null, undefined, []);
this.sync(serverConfigResource);
}

protected async loader(): Promise<ProductInfo> {
const { productInfo } = await this.graphQLService.sdk.getProductInfo();

return productInfo.productInfo;
}
}
Loading

0 comments on commit 9254684

Please sign in to comment.