Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PM-5237] Clients, Self Hosted: Login - Hide "Create account" when registration disabled #11811

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/web/src/app/auth/login/login-v1.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@
</a>
</div>

<hr />
<hr *ngIf="!(isUserRegistrationDisabled$ | async)" />

<p class="tw-m-0 tw-text-sm">
<p class="tw-m-0 tw-text-sm" *ngIf="!(isUserRegistrationDisabled$ | async)">
{{ "newAroundHere" | i18n }}
<!-- Two notes:
(1) We check the value and validity of email so we don't send an invalid email to autofill
Expand Down
12 changes: 10 additions & 2 deletions apps/web/src/app/auth/login/login-v1.component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Component, NgZone, OnInit } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { firstValueFrom, takeUntil } from "rxjs";
import { first } from "rxjs/operators";
import { firstValueFrom, Observable, takeUntil } from "rxjs";
import { first, map } from "rxjs/operators";

Check warning on line 5 in apps/web/src/app/auth/login/login-v1.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/auth/login/login-v1.component.ts#L4-L5

Added lines #L4 - L5 were not covered by tests

import { LoginComponentV1 as BaseLoginComponent } from "@bitwarden/angular/auth/components/login-v1.component";
import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service";
Expand All @@ -27,6 +27,7 @@
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ServerSettingsService } from "@bitwarden/common/platform/services/server-settings.service";

Check warning on line 30 in apps/web/src/app/auth/login/login-v1.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/auth/login/login-v1.component.ts#L30

Added line #L30 was not covered by tests
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { UserId } from "@bitwarden/common/types/guid";
import { ToastService } from "@bitwarden/components";
Expand All @@ -47,6 +48,7 @@
enforcedPasswordPolicyOptions: MasterPasswordPolicyOptions;
policies: Policy[];
showPasswordless = false;
isUserRegistrationDisabled$: Observable<boolean>;

constructor(
private acceptOrganizationInviteService: AcceptOrganizationInviteService,
Expand Down Expand Up @@ -74,6 +76,7 @@
webAuthnLoginService: WebAuthnLoginServiceAbstraction,
registerRouteService: RegisterRouteService,
toastService: ToastService,
protected serverSettingsService: ServerSettingsService,

Check warning on line 79 in apps/web/src/app/auth/login/login-v1.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/auth/login/login-v1.component.ts#L79

Added line #L79 was not covered by tests
) {
super(
devicesApiService,
Expand Down Expand Up @@ -140,6 +143,11 @@
if (orgInvite != null) {
await this.initPasswordPolicies(orgInvite);
}

this.isUserRegistrationDisabled$ = this.serverSettingsService.isUserRegistrationDisabled$.pipe(

Check warning on line 147 in apps/web/src/app/auth/login/login-v1.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/auth/login/login-v1.component.ts#L147

Added line #L147 was not covered by tests
map((value) => value ?? false),
takeUntil(this.destroy$),
);
}

async goAfterLogIn(userId: UserId) {
Expand Down
6 changes: 6 additions & 0 deletions libs/angular/src/services/jslib-services.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
import { NoopNotificationsService } from "@bitwarden/common/platform/services/noop-notifications.service";
import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/default-sdk.service";
import { ServerSettingsService } from "@bitwarden/common/platform/services/server-settings.service";

Check warning on line 187 in libs/angular/src/services/jslib-services.module.ts

View check run for this annotation

Codecov / codecov/patch

libs/angular/src/services/jslib-services.module.ts#L187

Added line #L187 was not covered by tests
import { StateService } from "@bitwarden/common/platform/services/state.service";
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
Expand Down Expand Up @@ -1316,6 +1317,11 @@
InternalUserDecryptionOptionsServiceAbstraction,
],
}),
safeProvider({
provide: ServerSettingsService,
useClass: ServerSettingsService,
deps: [ConfigService],
}),
safeProvider({
provide: RegisterRouteService,
useClass: RegisterRouteService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,25 @@

import { JslibModule } from "@bitwarden/angular/jslib.module";
import { RegisterRouteService } from "@bitwarden/auth/common";
import { ServerSettingsService } from "@bitwarden/common/platform/services/server-settings.service";

Check warning on line 7 in libs/auth/src/angular/login/login-secondary-content.component.ts

View check run for this annotation

Codecov / codecov/patch

libs/auth/src/angular/login/login-secondary-content.component.ts#L7

Added line #L7 was not covered by tests
import { LinkModule } from "@bitwarden/components";

@Component({
standalone: true,
imports: [CommonModule, JslibModule, LinkModule, RouterModule],
template: `
<div class="tw-text-center">
<div class="tw-text-center" *ngIf="!(isUserRegistrationDisabled$ | async)">
{{ "newToBitwarden" | i18n }}
<a bitLink [routerLink]="registerRoute$ | async">{{ "createAccount" | i18n }}</a>
</div>
`,
})
export class LoginSecondaryContentComponent {
registerRouteService = inject(RegisterRouteService);
serverSettingsService = inject(ServerSettingsService);

Check warning on line 22 in libs/auth/src/angular/login/login-secondary-content.component.ts

View check run for this annotation

Codecov / codecov/patch

libs/auth/src/angular/login/login-secondary-content.component.ts#L22

Added line #L22 was not covered by tests

// TODO: remove when email verification flag is removed
protected registerRoute$ = this.registerRouteService.registerRoute$();

protected isUserRegistrationDisabled$ = this.serverSettingsService.isUserRegistrationDisabled$;

Check warning on line 27 in libs/auth/src/angular/login/login-secondary-content.component.ts

View check run for this annotation

Codecov / codecov/patch

libs/auth/src/angular/login/login-secondary-content.component.ts#L27

Added line #L27 was not covered by tests
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import { SemVer } from "semver";

import { FeatureFlag, FeatureFlagValueType } from "../../../enums/feature-flag.enum";
import { UserId } from "../../../types/guid";
import { ServerSettings } from "../../models/domain/server-settings";
import { Region } from "../environment.service";

import { ServerConfig } from "./server-config";

export abstract class ConfigService {
/** The server config of the currently active user */
serverConfig$: Observable<ServerConfig | null>;
/** The server settings of the currently active user */
serverSettings$: Observable<ServerSettings | null>;
/** The cloud region of the currently active user */
cloudRegion$: Observable<Region>;
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ThirdPartyServerConfigData,
EnvironmentServerConfigData,
} from "../../models/data/server-config.data";
import { ServerSettings } from "../../models/domain/server-settings";

const dayInMilliseconds = 24 * 3600 * 1000;

Expand All @@ -16,6 +17,7 @@ export class ServerConfig {
environment?: EnvironmentServerConfigData;
utcDate: Date;
featureStates: { [key: string]: AllowedFeatureFlagTypes } = {};
settings: ServerSettings;

constructor(serverConfigData: ServerConfigData) {
this.version = serverConfigData.version;
Expand All @@ -24,6 +26,7 @@ export class ServerConfig {
this.utcDate = new Date(serverConfigData.utcDate);
this.environment = serverConfigData.environment;
this.featureStates = serverConfigData.featureStates;
this.settings = serverConfigData.settings;

if (this.server?.name == null && this.server?.url == null) {
this.server = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ describe("ServerConfigData", () => {
name: "test",
url: "https://test.com",
},
settings: {
disableUserRegistration: false,
},
environment: {
cloudRegion: Region.EU,
vault: "https://vault.com",
Expand Down
3 changes: 3 additions & 0 deletions libs/common/src/platform/models/data/server-config.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Jsonify } from "type-fest";

import { AllowedFeatureFlagTypes } from "../../../enums/feature-flag.enum";
import { Region } from "../../abstractions/environment.service";
import { ServerSettings } from "../domain/server-settings";
import {
ServerConfigResponse,
ThirdPartyServerConfigResponse,
Expand All @@ -15,6 +16,7 @@ export class ServerConfigData {
environment?: EnvironmentServerConfigData;
utcDate: string;
featureStates: { [key: string]: AllowedFeatureFlagTypes } = {};
settings: ServerSettings;

constructor(serverConfigResponse: Partial<ServerConfigResponse>) {
this.version = serverConfigResponse?.version;
Expand All @@ -27,6 +29,7 @@ export class ServerConfigData {
? new EnvironmentServerConfigData(serverConfigResponse.environment)
: null;
this.featureStates = serverConfigResponse?.featureStates;
this.settings = new ServerSettings(serverConfigResponse.settings);
}

static fromJSON(obj: Jsonify<ServerConfigData>): ServerConfigData {
Expand Down
20 changes: 20 additions & 0 deletions libs/common/src/platform/models/domain/server-settings.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ServerSettings } from "./server-settings";

describe("ServerSettings", () => {
describe("disableUserRegistration", () => {
it("defaults disableUserRegistration to false", () => {
const settings = new ServerSettings();
expect(settings.disableUserRegistration).toBe(false);
});

it("sets disableUserRegistration to true when provided", () => {
const settings = new ServerSettings({ disableUserRegistration: true });
expect(settings.disableUserRegistration).toBe(true);
});

it("sets disableUserRegistration to false when provided", () => {
const settings = new ServerSettings({ disableUserRegistration: false });
expect(settings.disableUserRegistration).toBe(false);
});
});
});
11 changes: 11 additions & 0 deletions libs/common/src/platform/models/domain/server-settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface Settings {
disableUserRegistration: boolean;
}

export class ServerSettings implements Settings {
disableUserRegistration: boolean;

constructor(data?: Settings) {
this.disableUserRegistration = data?.disableUserRegistration ?? false;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { AllowedFeatureFlagTypes } from "../../../enums/feature-flag.enum";
import { BaseResponse } from "../../../models/response/base.response";
import { Region } from "../../abstractions/environment.service";
import { ServerSettings } from "../domain/server-settings";

export class ServerConfigResponse extends BaseResponse {
version: string;
gitHash: string;
server: ThirdPartyServerConfigResponse;
environment: EnvironmentServerConfigResponse;
featureStates: { [key: string]: AllowedFeatureFlagTypes } = {};
settings: ServerSettings;

constructor(response: any) {
super(response);
Expand All @@ -21,6 +23,7 @@ export class ServerConfigResponse extends BaseResponse {
this.server = new ThirdPartyServerConfigResponse(this.getResponseProperty("Server"));
this.environment = new EnvironmentServerConfigResponse(this.getResponseProperty("Environment"));
this.featureStates = this.getResponseProperty("FeatureStates");
this.settings = new ServerSettings(this.getResponseProperty("Settings"));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { Environment, EnvironmentService, Region } from "../../abstractions/envi
import { LogService } from "../../abstractions/log.service";
import { devFlagEnabled, devFlagValue } from "../../misc/flags";
import { ServerConfigData } from "../../models/data/server-config.data";
import { ServerSettings } from "../../models/domain/server-settings";
import { CONFIG_DISK, KeyDefinition, StateProvider, UserKeyDefinition } from "../../state";

export const RETRIEVAL_INTERVAL = devFlagEnabled("configRetrievalIntervalMs")
Expand Down Expand Up @@ -57,6 +58,8 @@ export class DefaultConfigService implements ConfigService {

serverConfig$: Observable<ServerConfig>;

serverSettings$: Observable<ServerSettings>;

cloudRegion$: Observable<Region>;

constructor(
Expand Down Expand Up @@ -111,6 +114,10 @@ export class DefaultConfigService implements ConfigService {
this.cloudRegion$ = this.serverConfig$.pipe(
map((config) => config?.environment?.cloudRegion ?? Region.US),
);

this.serverSettings$ = this.serverConfig$.pipe(
map((config) => config?.settings ?? new ServerSettings()),
);
}

getFeatureFlag$<Flag extends FeatureFlag>(key: Flag) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { of } from "rxjs";

import { ConfigService } from "../abstractions/config/config.service";
import { ServerSettings } from "../models/domain/server-settings";

import { ServerSettingsService } from "./server-settings.service";

describe("ServerSettingsService", () => {
let service: ServerSettingsService;
let configServiceMock: { serverSettings$: any };

beforeEach(() => {
configServiceMock = { serverSettings$: of() };
service = new ServerSettingsService(configServiceMock as ConfigService);
});

describe("getSettings$", () => {
it("returns server settings", () => {
const mockSettings = new ServerSettings({ disableUserRegistration: true });
configServiceMock.serverSettings$ = of(mockSettings);

service.getSettings$().subscribe((settings) => {
expect(settings).toEqual(mockSettings);
});
});
});

describe("isUserRegistrationDisabled$", () => {
it("returns true when user registration is disabled", () => {
const mockSettings = new ServerSettings({ disableUserRegistration: true });
configServiceMock.serverSettings$ = of(mockSettings);

service.isUserRegistrationDisabled$.subscribe((isDisabled: boolean) => {
expect(isDisabled).toBe(true);
});
});

it("returns false when user registration is enabled", () => {
const mockSettings = new ServerSettings({ disableUserRegistration: false });
configServiceMock.serverSettings$ = of(mockSettings);

service.isUserRegistrationDisabled$.subscribe((isDisabled: boolean) => {
expect(isDisabled).toBe(false);
});
});
});
});
16 changes: 16 additions & 0 deletions libs/common/src/platform/services/server-settings.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { map, Observable } from "rxjs";

import { ConfigService } from "../abstractions/config/config.service";
import { ServerSettings, Settings } from "../models/domain/server-settings";

export class ServerSettingsService {
constructor(private configService: ConfigService) {}

getSettings$(): Observable<Settings> {
return this.configService.serverSettings$.pipe(map((settings: ServerSettings) => settings));
}

get isUserRegistrationDisabled$(): Observable<boolean> {
return this.getSettings$().pipe(map((settings: Settings) => settings.disableUserRegistration));
}
}
Loading