From f5eee4b4dc78e44465b12d79cb4febddddb769b5 Mon Sep 17 00:00:00 2001 From: gobdevel Date: Thu, 15 Dec 2022 14:55:43 +0530 Subject: [PATCH] Add knob to control user signup feature Add knob "Disable User Signup" in Admin overview page Add new permission to handle this knob, only admin has access Add new admin property to store this knob config Validate user signup against this config. --- apps/api/src/app/info/info.service.ts | 13 +++++++++++++ apps/api/src/app/user/user.controller.ts | 18 +++++++++++++++++- apps/api/src/app/user/user.service.ts | 6 ++++++ apps/api/src/services/configuration.service.ts | 1 + .../interfaces/environment.interface.ts | 1 + .../admin-overview/admin-overview.component.ts | 16 +++++++++++++++- .../admin-overview/admin-overview.html | 11 +++++++++++ libs/common/src/lib/config.ts | 1 + libs/common/src/lib/permissions.ts | 3 ++- 9 files changed, 67 insertions(+), 3 deletions(-) diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index 8ed589cb5..d8d8420af 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -11,6 +11,7 @@ import { PROPERTY_SLACK_COMMUNITY_USERS, PROPERTY_STRIPE_CONFIG, PROPERTY_SYSTEM_MESSAGE, + PROPERTY_DISABLE_USER_SIGNUP, ghostfolioFearAndGreedIndexDataSource } from '@ghostfolio/common/config'; import { @@ -103,6 +104,18 @@ export class InfoService { )) as string; } + if (this.configurationService.get('ENABLE_FEATURE_USER_SIGNUP_CONTROL')) { + const isUserSignupDisabled = (await this.propertyService.getByKey( + PROPERTY_DISABLE_USER_SIGNUP + )) as boolean; + + if (!isUserSignupDisabled) { + globalPermissions.push(permissions.createUserAccount); + } + } else { // By default enabled + globalPermissions.push(permissions.createUserAccount); + } + return { ...info, globalPermissions, diff --git a/apps/api/src/app/user/user.controller.ts b/apps/api/src/app/user/user.controller.ts index 2b1f063bf..f2f52b114 100644 --- a/apps/api/src/app/user/user.controller.ts +++ b/apps/api/src/app/user/user.controller.ts @@ -1,6 +1,9 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; -import { PROPERTY_IS_READ_ONLY_MODE } from '@ghostfolio/common/config'; +import { + PROPERTY_IS_READ_ONLY_MODE, + PROPERTY_DISABLE_USER_SIGNUP +} from '@ghostfolio/common/config'; import { User, UserSettings } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import type { RequestWithUser } from '@ghostfolio/common/types'; @@ -82,6 +85,19 @@ export class UserController { } } + if (this.configurationService.get('ENABLE_FEATURE_USER_SIGNUP_CONTROL')) { + const isUserSignupDisabled = (await this.propertyService.getByKey( + PROPERTY_DISABLE_USER_SIGNUP + )) as boolean; + + if (isUserSignupDisabled) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + } + const hasAdmin = await this.userService.hasAdmin(); const { accessToken, id, role } = await this.userService.createUser({ diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 13f82aa47..8b7a7dedd 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -185,6 +185,12 @@ export class UserService { } } + if (this.configurationService.get('ENABLE_FEATURE_USER_SIGNUP_CONTROL')) { + if (hasRole(user, Role.ADMIN)) { + currentPermissions.push(permissions.toggleUserSignupMode); + } + } + user.Account = sortBy(user.Account, (account) => { return account.name; }); diff --git a/apps/api/src/services/configuration.service.ts b/apps/api/src/services/configuration.service.ts index 75441c8c3..c077b8b69 100644 --- a/apps/api/src/services/configuration.service.ts +++ b/apps/api/src/services/configuration.service.ts @@ -30,6 +30,7 @@ export class ConfigurationService { ENABLE_FEATURE_STATISTICS: bool({ default: false }), ENABLE_FEATURE_SUBSCRIPTION: bool({ default: false }), ENABLE_FEATURE_SYSTEM_MESSAGE: bool({ default: false }), + ENABLE_FEATURE_USER_SIGNUP_CONTROL: bool({ default: false }), EOD_HISTORICAL_DATA_API_KEY: str({ default: '' }), GOOGLE_CLIENT_ID: str({ default: 'dummyClientId' }), GOOGLE_SECRET: str({ default: 'dummySecret' }), diff --git a/apps/api/src/services/interfaces/environment.interface.ts b/apps/api/src/services/interfaces/environment.interface.ts index 5ac20b55e..7011c1a8f 100644 --- a/apps/api/src/services/interfaces/environment.interface.ts +++ b/apps/api/src/services/interfaces/environment.interface.ts @@ -16,6 +16,7 @@ export interface Environment extends CleanedEnvAccessors { ENABLE_FEATURE_STATISTICS: boolean; ENABLE_FEATURE_SUBSCRIPTION: boolean; ENABLE_FEATURE_SYSTEM_MESSAGE: boolean; + ENABLE_FEATURE_USER_SIGNUP_CONTROL: boolean; EOD_HISTORICAL_DATA_API_KEY: string; GOOGLE_CLIENT_ID: string; GOOGLE_SECRET: string; diff --git a/apps/client/src/app/components/admin-overview/admin-overview.component.ts b/apps/client/src/app/components/admin-overview/admin-overview.component.ts index 033f78c69..ab355391e 100644 --- a/apps/client/src/app/components/admin-overview/admin-overview.component.ts +++ b/apps/client/src/app/components/admin-overview/admin-overview.component.ts @@ -7,7 +7,8 @@ import { PROPERTY_COUPONS, PROPERTY_CURRENCIES, PROPERTY_IS_READ_ONLY_MODE, - PROPERTY_SYSTEM_MESSAGE + PROPERTY_SYSTEM_MESSAGE, + PROPERTY_DISABLE_USER_SIGNUP } from '@ghostfolio/common/config'; import { Coupon, InfoItem, User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; @@ -34,6 +35,7 @@ export class AdminOverviewComponent implements OnDestroy, OnInit { public hasPermissionForSubscription: boolean; public hasPermissionForSystemMessage: boolean; public hasPermissionToToggleReadOnlyMode: boolean; + public hasPermissionToToggleUserSignupMode: boolean; public info: InfoItem; public transactionCount: number; public userCount: number; @@ -69,6 +71,11 @@ export class AdminOverviewComponent implements OnDestroy, OnInit { this.user.permissions, permissions.toggleReadOnlyMode ); + + this.hasPermissionToToggleUserSignupMode = hasPermission( + this.user.permissions, + permissions.toggleUserSignupMode + ); } }); } @@ -167,6 +174,13 @@ export class AdminOverviewComponent implements OnDestroy, OnInit { }); } + public onEnableUserSignupModeChange(aEvent: MatSlideToggleChange) { + this.putAdminSetting({ + key: PROPERTY_DISABLE_USER_SIGNUP, + value: aEvent.checked ? true : undefined + }); + } + public onSetSystemMessage() { const systemMessage = prompt($localize`Please set your system message:`); diff --git a/apps/client/src/app/components/admin-overview/admin-overview.html b/apps/client/src/app/components/admin-overview/admin-overview.html index c9414201b..38408c04f 100644 --- a/apps/client/src/app/components/admin-overview/admin-overview.html +++ b/apps/client/src/app/components/admin-overview/admin-overview.html @@ -119,6 +119,17 @@ > +
+
Disable User Signup
+
+ +
+
[ 'active', diff --git a/libs/common/src/lib/permissions.ts b/libs/common/src/lib/permissions.ts index 6be7a3401..9785102ad 100644 --- a/libs/common/src/lib/permissions.ts +++ b/libs/common/src/lib/permissions.ts @@ -26,7 +26,8 @@ export const permissions = { updateAuthDevice: 'updateAuthDevice', updateOrder: 'updateOrder', updateUserSettings: 'updateUserSettings', - updateViewMode: 'updateViewMode' + updateViewMode: 'updateViewMode', + toggleUserSignupMode: 'toggleUserSignupMode' }; export function getPermissions(aRole: Role): string[] {