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 @@ > +