diff --git a/.dockerignore b/.dockerignore index 1f89afb..6ebf81e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,11 +8,18 @@ web-vault *.md *.txt -# Other +# IDE files +.vscode +.idea +*.iml + +# Environment files .env +.build_env + +# Other .github Makefile -.vscode # Release files *.tar.gz diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..3d036b8 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +/.github @dani-garcia @BlackDex +/.github/CODEOWNERS @dani-garcia @BlackDex +/.github/workflows/** @dani-garcia @BlackDex diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6e92bbd --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: CI + +on: + pull_request: + paths: + - ".github/workflows/ci.yml" + - "patches/v20*.patch" + - "resources/**" + - "scripts/**" + - "Dockerfile" + +jobs: + docker-build: + runs-on: ubuntu-latest + env: + # Force docker to output the progress in plain + BUILDKIT_PROGRESS: plain + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Build and Extract + shell: bash + run: | + make docker-extract + + - name: Generate checksum + shell: bash + run: | + echo "sha256sum: $(sha256sum container_builds/bw_web_vault.tar.gz)" + + - name: "Upload web-vault artifact" + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + with: + name: web-vault + compression-level: 0 + path: container_builds/bw_web_vault.tar.gz diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dffd084..667fc1c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: HAVE_GHCR_LOGIN: ${{ vars.GHCR_REPO != '' && github.repository_owner != '' && secrets.GITHUB_TOKEN != '' }} steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 # Determine Docker Tag - name: Init Variables @@ -64,7 +64,7 @@ jobs: | tee -a "${GITHUB_ENV}" - name: Build and push - uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 # v6.5.0 + uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0 with: context: . push: true diff --git a/.gitignore b/.gitignore index c015c05..5137943 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,17 @@ # Local build artifacts -builds/ -docker_builds/ -container_builds/ -web-vault/ +builds +docker_builds +container_builds +web-vault -# Other +# IDE files +.vscode +.idea +*.iml + +# Environment files .env .build_env -.vscode # Release files *.tar.gz diff --git a/Dockerfile b/Dockerfile index 4427ed9..a19fa9d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,8 +23,8 @@ RUN node --version && npm --version # Can be a tag, release, but prefer a commit hash because it's not changeable # https://github.com/bitwarden/clients/commit/${VAULT_VERSION} # -# Using https://github.com/bitwarden/clients/releases/tag/web-v2024.6.2 -ARG VAULT_VERSION=e2354e8694ab5e532d04f275e4bd6bf560c7509b +# Using https://github.com/bitwarden/clients/releases/tag/web-v2024.12.0 +ARG VAULT_VERSION=e0c8f2ced997fc279c8ded903471ca3fa0fe5e94 ENV VAULT_VERSION=$VAULT_VERSION ENV VAULT_FOLDER=bw_clients ENV CHECKOUT_TAGS=false @@ -45,7 +45,7 @@ RUN mv "${VAULT_FOLDER}/apps/web/build" ./web-vault RUN tar -czvf "bw_web_vault.tar.gz" web-vault --owner=0 --group=0 # Output the sha256sum here so people are able to match the sha256sum from the CI with the assets and the downloaded version if needed -RUN echo "sha256sum: $(sha256sum "bw_web_vault.tar.gz")" +RUN echo "sha256sum: $(sha256sum bw_web_vault.tar.gz)" # We copy the final result as a separate empty image so there's no need to download all the intermediate steps # The result is included both uncompressed and as a tar.gz, to be able to use it in the docker images and the github releases directly diff --git a/patches/v2023.1.0.patch b/patches/legacy/v2023.1.0.patch similarity index 100% rename from patches/v2023.1.0.patch rename to patches/legacy/v2023.1.0.patch diff --git a/patches/v2023.1.1.patch b/patches/legacy/v2023.1.1.patch similarity index 100% rename from patches/v2023.1.1.patch rename to patches/legacy/v2023.1.1.patch diff --git a/patches/v2023.10.0.patch b/patches/legacy/v2023.10.0.patch similarity index 100% rename from patches/v2023.10.0.patch rename to patches/legacy/v2023.10.0.patch diff --git a/patches/v2023.12.0.patch b/patches/legacy/v2023.12.0.patch similarity index 100% rename from patches/v2023.12.0.patch rename to patches/legacy/v2023.12.0.patch diff --git a/patches/v2023.2.0.patch b/patches/legacy/v2023.2.0.patch similarity index 100% rename from patches/v2023.2.0.patch rename to patches/legacy/v2023.2.0.patch diff --git a/patches/v2023.3.0.patch b/patches/legacy/v2023.3.0.patch similarity index 100% rename from patches/v2023.3.0.patch rename to patches/legacy/v2023.3.0.patch diff --git a/patches/v2023.4.0.patch b/patches/legacy/v2023.4.0.patch similarity index 100% rename from patches/v2023.4.0.patch rename to patches/legacy/v2023.4.0.patch diff --git a/patches/v2023.5.0.patch b/patches/legacy/v2023.5.0.patch similarity index 100% rename from patches/v2023.5.0.patch rename to patches/legacy/v2023.5.0.patch diff --git a/patches/v2023.5.1.patch b/patches/legacy/v2023.5.1.patch similarity index 100% rename from patches/v2023.5.1.patch rename to patches/legacy/v2023.5.1.patch diff --git a/patches/v2023.7.1.patch b/patches/legacy/v2023.7.1.patch similarity index 100% rename from patches/v2023.7.1.patch rename to patches/legacy/v2023.7.1.patch diff --git a/patches/v2023.8.2.patch b/patches/legacy/v2023.8.2.patch similarity index 100% rename from patches/v2023.8.2.patch rename to patches/legacy/v2023.8.2.patch diff --git a/patches/v2023.9.1.patch b/patches/legacy/v2023.9.1.patch similarity index 100% rename from patches/v2023.9.1.patch rename to patches/legacy/v2023.9.1.patch diff --git a/patches/v2024.10.5.patch b/patches/v2024.10.5.patch new file mode 100644 index 0000000..fc4bf44 --- /dev/null +++ b/patches/v2024.10.5.patch @@ -0,0 +1,848 @@ +diff --git a/apps/web/package.json b/apps/web/package.json +index 21274fbd80..3cfbc278a6 100644 +--- a/apps/web/package.json ++++ b/apps/web/package.json +@@ -3,11 +3,8 @@ + "version": "2024.10.5", + "scripts": { + "build:oss": "webpack", +- "build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js", + "build:oss:watch": "webpack serve", +- "build:bit:watch": "webpack serve -c ../../bitwarden_license/bit-web/webpack.config.js", + "build:bit:dev": "cross-env ENV=development npm run build:bit", +- "build:bit:dev:analyze": "cross-env LOGGING=false webpack -c ../../bitwarden_license/bit-web/webpack.config.js --profile --json > stats.json && npx webpack-bundle-analyzer stats.json build/", + "build:bit:dev:watch": "cross-env ENV=development NODE_OPTIONS=\"--max-old-space-size=8192\" npm run build:bit:watch", + "build:bit:qa": "cross-env NODE_ENV=production ENV=qa npm run build:bit", + "build:bit:euprd": "cross-env NODE_ENV=production ENV=euprd npm run build:bit", +diff --git a/apps/web/src/app/admin-console/organizations/create/organization-information.component.html b/apps/web/src/app/admin-console/organizations/create/organization-information.component.html +index e0a8006081..789efd9264 100644 +--- a/apps/web/src/app/admin-console/organizations/create/organization-information.component.html ++++ b/apps/web/src/app/admin-console/organizations/create/organization-information.component.html +@@ -12,7 +12,7 @@ + + + +- {{ "billingEmail" | i18n }} ++ {{ "email" | i18n }} + + + +diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts +index 0beb87c81c..d1e5961c2b 100644 +--- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts ++++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts +@@ -142,6 +142,7 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy { + } + + canShowBillingTab(organization: Organization): boolean { ++ return false; // disable billing tab in Vaultwarden + return canAccessBillingTab(organization); + } + +diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.ts b/apps/web/src/app/admin-console/organizations/members/members.component.ts +index 3cc73c84a9..4f8f756298 100644 +--- a/apps/web/src/app/admin-console/organizations/members/members.component.ts ++++ b/apps/web/src/app/admin-console/organizations/members/members.component.ts +@@ -193,11 +193,7 @@ export class MembersComponent extends BaseMembersComponent + .find((p) => p.organizationId === this.organization.id); + this.orgResetPasswordPolicyEnabled = resetPasswordPolicy?.enabled; + +- const billingMetadata = await this.billingApiService.getOrganizationBillingMetadata( +- this.organization.id, +- ); +- +- this.orgIsOnSecretsManagerStandalone = billingMetadata.isOnSecretsManagerStandalone; ++ this.orgIsOnSecretsManagerStandalone = false; // don't get billing metadata + + await this.load(); + +diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.html b/apps/web/src/app/admin-console/organizations/settings/account.component.html +index d1a1a09192..0df5c1533d 100644 +--- a/apps/web/src/app/admin-console/organizations/settings/account.component.html ++++ b/apps/web/src/app/admin-console/organizations/settings/account.component.html +@@ -16,7 +16,7 @@ + + + +- {{ "billingEmail" | i18n }} ++ {{ "email" | i18n }} + + + +diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.ts b/apps/web/src/app/admin-console/organizations/settings/account.component.ts +index 67e94fad37..b27d532d98 100644 +--- a/apps/web/src/app/admin-console/organizations/settings/account.component.ts ++++ b/apps/web/src/app/admin-console/organizations/settings/account.component.ts +@@ -96,7 +96,7 @@ export class AccountComponent implements OnInit, OnDestroy { + ) {} + + async ngOnInit() { +- this.selfHosted = this.platformUtilsService.isSelfHost(); ++ this.selfHosted = false; // set to false so we can rename organizations + + this.configService + .getFeatureFlag$(FeatureFlag.LimitCollectionCreationDeletionSplit) +@@ -209,6 +209,7 @@ export class AccountComponent implements OnInit, OnDestroy { + }; + + submitCollectionManagement = async () => { ++ return; // flexible collections are not supported by Vaultwarden + // Early exit if self-hosted + if (this.selfHosted && !this.limitCollectionCreationDeletionSplitFeatureFlagIsEnabled) { + return; +diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts +index 7cefdd2165..1573c0ce5f 100644 +--- a/apps/web/src/app/app.component.ts ++++ b/apps/web/src/app/app.component.ts +@@ -232,6 +232,10 @@ export class AppComponent implements OnDestroy, OnInit { + break; + } + case "showToast": ++ if (typeof message.text === "string" && typeof crypto.subtle === "undefined") { ++ message.title = "This browser requires HTTPS to use the web vault"; ++ message.text = "Check the Vaultwarden wiki for details on how to enable it"; ++ } + this.toastService._showToast(message); + break; + case "convertAccountToKeyConnector": +diff --git a/apps/web/src/app/auth/login/login.component.html b/apps/web/src/app/auth/login/login.component.html +index d26e81b35b..3897dc6abc 100644 +--- a/apps/web/src/app/auth/login/login.component.html ++++ b/apps/web/src/app/auth/login/login.component.html +@@ -30,7 +30,7 @@ + + + +-
++
+

{{ "or" | i18n }}

+ + + +- +- +diff --git a/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts b/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts +index da5378f479..6d80d96ff5 100644 +--- a/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts ++++ b/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts +@@ -187,11 +187,11 @@ export class TwoFactorAuthenticatorComponent + new window.QRious({ + element: document.getElementById("qr"), + value: +- "otpauth://totp/Bitwarden:" + ++ "otpauth://totp/Vaultwarden:" + + Utils.encodeRFC3986URIComponent(email) + + "?secret=" + + encodeURIComponent(this.key) + +- "&issuer=Bitwarden", ++ "&issuer=Vaultwarden", + size: 160, + }); + } +diff --git a/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts b/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts +index 30ae39d481..37e2db43d2 100644 +--- a/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts ++++ b/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts +@@ -52,6 +52,7 @@ export class OrgBillingHistoryViewComponent implements OnInit, OnDestroy { + + this.loading = true; + ++ /* disable billing history + const openInvoicesPromise = this.organizationBillingApiService.getBillingInvoices( + this.organizationId, + "open", +@@ -85,6 +86,7 @@ export class OrgBillingHistoryViewComponent implements OnInit, OnDestroy { + openInvoices.length <= pageSize || + paidInvoices.length <= pageSize || + transactions.length <= pageSize; ++ */ + + this.loading = false; + } +diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.html b/apps/web/src/app/billing/organizations/organization-plans.component.html +index 16c5259e8a..3f758ff68d 100644 +--- a/apps/web/src/app/billing/organizations/organization-plans.component.html ++++ b/apps/web/src/app/billing/organizations/organization-plans.component.html +@@ -6,7 +6,7 @@ + > + {{ "loading" | i18n }} + +- ++ + +

{{ "uploadLicenseFileOrg" | i18n }}

+
+@@ -40,12 +40,7 @@ + (onLicenseFileUploaded)="onLicenseFileUploaded($event)" + /> + +- ++ + + !!plan.PasswordManager); +@@ -216,6 +217,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + this.plan = providerDefaultPlan.type; + this.productTier = providerDefaultPlan.productTier; + } ++ end of asking /api/plans in Vaultwarden */ + + if (!this.createOrganization) { + this.upgradeFlowPrefillForm(); +@@ -290,6 +292,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + get selectableProducts() { ++ return null; // there are no products to select in Vaultwarden + if (this.acceptingSponsorship) { + const familyPlan = this.passwordManagerPlans.find( + (plan) => plan.type === PlanType.FamiliesAnnually, +@@ -321,6 +324,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + get selectablePlans() { ++ return null; // no plans to select in Vaultwarden + const selectedProductTierType = this.formGroup.controls.productTier.value; + const result = + this.passwordManagerPlans?.filter( +@@ -462,6 +466,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + get planOffersSecretsManager() { ++ return false; // no support for secrets manager in Vaultwarden + return this.selectedSecretsManagerPlan != null; + } + +@@ -470,6 +475,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + changedProduct() { ++ return; // no choice of products in Vaultwarden + const selectedPlan = this.selectablePlans[0]; + + this.setPlanType(selectedPlan.type); +@@ -605,11 +611,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + const collectionCt = collection.encryptedString; + const orgKeys = await this.cryptoService.makeKeyPair(orgKey[1]); + +- if (this.selfHosted) { +- orgId = await this.createSelfHosted(key, collectionCt, orgKeys); +- } else { +- orgId = await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]); +- } ++ // always use createCloudHosted() to disable license file upload ++ orgId = await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]); + + this.toastService.showToast({ + variant: "success", +@@ -715,7 +718,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + request.billingEmail = this.formGroup.controls.billingEmail.value; + request.initiationPath = "New organization creation in-product"; + request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString); ++ request.planType = PlanType.Free; // always select the free plan in Vaultwarden + ++ /* there is no plan to select in Vaultwarden + if (this.selectedPlan.type === PlanType.Free) { + request.planType = PlanType.Free; + } else { +@@ -749,6 +754,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + + // Secrets Manager + this.buildSecretsManagerRequest(request); ++ end plan selection and no support for secret manager in Vaultwarden */ + + if (this.hasProvider) { + const providerRequest = new ProviderOrganizationCreateRequest( +@@ -828,6 +834,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + private upgradeFlowPrefillForm() { ++ return; // Vaultwarden only supports free plan + if (this.acceptingSponsorship) { + this.formGroup.controls.productTier.setValue(ProductTierType.Families); + this.changedProduct(); +diff --git a/apps/web/src/app/core/router.service.ts b/apps/web/src/app/core/router.service.ts +index 0790162848..5728616cc8 100644 +--- a/apps/web/src/app/core/router.service.ts ++++ b/apps/web/src/app/core/router.service.ts +@@ -59,7 +59,7 @@ export class RouterService { + .subscribe((event: NavigationEnd) => { + this.currentUrl = event.url; + +- let title = i18nService.t("bitWebVault"); ++ let title = "Vaultwarden Web"; + + if (this.currentUrl.includes("/sm/")) { + title = i18nService.t("bitSecretsManager"); +diff --git a/apps/web/src/app/core/web-platform-utils.service.ts b/apps/web/src/app/core/web-platform-utils.service.ts +index dbd0ef593d..3452567592 100644 +--- a/apps/web/src/app/core/web-platform-utils.service.ts ++++ b/apps/web/src/app/core/web-platform-utils.service.ts +@@ -133,14 +133,17 @@ export class WebPlatformUtilsService implements PlatformUtilsService { + } + + isDev(): boolean { ++ return false; // treat Vaultwarden as production ready + return process.env.NODE_ENV === "development"; + } + + isSelfHost(): boolean { ++ return true; // treat Vaultwarden as self hosted + return WebPlatformUtilsService.isSelfHost(); + } + + static isSelfHost(): boolean { ++ return true; // treat Vaultwarden as self hosted + return process.env.ENV.toString() === "selfhosted"; + } + +diff --git a/apps/web/src/app/layouts/frontend-layout.component.html b/apps/web/src/app/layouts/frontend-layout.component.html +index 72f0f1f1da..cea0867131 100644 +--- a/apps/web/src/app/layouts/frontend-layout.component.html ++++ b/apps/web/src/app/layouts/frontend-layout.component.html +@@ -1,6 +1,11 @@ + +
+- +- © {{ year }} Bitwarden Inc.
++ Vaultwarden Web
+ {{ "versionNumber" | i18n: version }} ++

++
++ A modified version of the Bitwarden® Web Vault for Vaultwarden (an unofficial rewrite of the ++ Bitwarden® server).
++ Vaultwarden is not associated with the Bitwarden® project nor Bitwarden Inc. ++
+
+diff --git a/apps/web/src/app/layouts/header/web-header.component.html b/apps/web/src/app/layouts/header/web-header.component.html +index 7cba19b29a..024b14daad 100644 +--- a/apps/web/src/app/layouts/header/web-header.component.html ++++ b/apps/web/src/app/layouts/header/web-header.component.html +@@ -61,7 +61,12 @@ + + {{ "accountSettings" | i18n }} +
+- ++ + + {{ "getHelp" | i18n }} + +diff --git a/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html b/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html +index 08195d95bf..f51a216a14 100644 +--- a/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html ++++ b/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html +@@ -10,10 +10,7 @@ + > + + +-
++
+ {{ "moreFromBitwarden" | i18n }} + + +diff --git a/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html b/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html +index 41346675bb..d366fdf7d7 100644 +--- a/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html ++++ b/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html +@@ -30,7 +30,7 @@ + + +
+ {{ "moreFromBitwarden" | i18n }} +diff --git a/apps/web/src/app/layouts/user-layout.component.ts b/apps/web/src/app/layouts/user-layout.component.ts +index bd02533233..072465f7af 100644 +--- a/apps/web/src/app/layouts/user-layout.component.ts ++++ b/apps/web/src/app/layouts/user-layout.component.ts +@@ -1,7 +1,7 @@ + import { CommonModule } from "@angular/common"; + import { Component, OnInit } from "@angular/core"; + import { RouterModule } from "@angular/router"; +-import { Observable, combineLatest, concatMap } from "rxjs"; ++import { Observable, of } from "rxjs"; + + import { JslibModule } from "@bitwarden/angular/jslib.module"; + import { ApiService } from "@bitwarden/common/abstractions/api.service"; +@@ -38,26 +38,7 @@ export class UserLayoutComponent implements OnInit { + + await this.syncService.fullSync(false); + +- this.hasFamilySponsorshipAvailable$ = this.organizationService.canManageSponsorships$; +- +- // We want to hide the subscription menu for organizations that provide premium. +- // Except if the user has premium personally or has a billing history. +- this.showSubscription$ = combineLatest([ +- this.billingAccountProfileStateService.hasPremiumPersonally$, +- this.billingAccountProfileStateService.hasPremiumFromAnyOrganization$, +- ]).pipe( +- concatMap(async ([hasPremiumPersonally, hasPremiumFromOrg]) => { +- const isCloud = !this.platformUtilsService.isSelfHost(); +- +- let billing = null; +- if (isCloud) { +- // TODO: We should remove the need to call this! +- billing = await this.apiService.getUserBillingHistory(); +- } +- +- const cloudAndBillingHistory = isCloud && !billing?.hasNoHistory; +- return hasPremiumPersonally || !hasPremiumFromOrg || cloudAndBillingHistory; +- }), +- ); ++ this.hasFamilySponsorshipAvailable$ = of(false); // disable family Sponsorships in Vaultwarden ++ this.showSubscription$ = of(false); // always hide subscriptions in Vaultwarden + } + } +diff --git a/apps/web/src/app/platform/web-environment.service.ts b/apps/web/src/app/platform/web-environment.service.ts +index ebddc7491b..7e12389a4a 100644 +--- a/apps/web/src/app/platform/web-environment.service.ts ++++ b/apps/web/src/app/platform/web-environment.service.ts +@@ -36,7 +36,17 @@ export class WebEnvironmentService extends DefaultEnvironmentService { + super(stateProvider, accountService, additionalRegionConfigs); + + // The web vault always uses the current location as the base url +- envUrls.base ??= this.win.location.origin; ++ // If the base URL is `https://vaultwarden.example.com/base/path/`, ++ // `window.location.href` should have one of the following forms: ++ // ++ // - `https://vaultwarden.example.com/base/path/` ++ // - `https://vaultwarden.example.com/base/path/#/some/route[?queryParam=...]` ++ // - `https://vaultwarden.example.com/base/path/?queryParam=...` ++ // ++ // We want to get to just `https://vaultwarden.example.com/base/path`. ++ let baseUrl = this.win.location.href; ++ baseUrl = baseUrl.replace(/(\/+|\/*#.*|\/*\?.*)$/, ""); // Strip off trailing `/`, `#`, `?` and everything after. ++ envUrls.base ??= baseUrl; + + // Find the region + const currentHostname = new URL(this.win.location.href).hostname; +diff --git a/apps/web/src/app/tools/send/add-edit.component.html b/apps/web/src/app/tools/send/add-edit.component.html +index 7eade18a7c..b8f865faa7 100644 +--- a/apps/web/src/app/tools/send/add-edit.component.html ++++ b/apps/web/src/app/tools/send/add-edit.component.html +@@ -230,7 +230,12 @@ + {{ "password" | i18n }} + {{ "newPassword" | i18n }} + +- ++ + + {{ "sendPasswordDesc" | i18n }} + +diff --git a/apps/web/src/app/tools/send/send-access-explainer.component.html b/apps/web/src/app/tools/send/send-access-explainer.component.html +index e8090cb850..284b916514 100644 +--- a/apps/web/src/app/tools/send/send-access-explainer.component.html ++++ b/apps/web/src/app/tools/send/send-access-explainer.component.html +@@ -1,18 +1,5 @@ +
+

+ {{ "sendAccessTaglineProductDesc" | i18n }} +- {{ "sendAccessTaglineLearnMore" | i18n }} +- Bitwarden Send +- {{ "sendAccessTaglineOr" | i18n }} +- {{ +- "sendAccessTaglineSignUp" | i18n +- }} +- {{ "sendAccessTaglineTryToday" | i18n }} +

+
+diff --git a/apps/web/src/index.html b/apps/web/src/index.html +index ce1a955b88..ec0f30b18c 100644 +--- a/apps/web/src/index.html ++++ b/apps/web/src/index.html +@@ -1,11 +1,11 @@ +- ++ + + + + + + +- Bitwarden Web vault ++ Vaultwarden Web + + + +@@ -16,7 +16,7 @@ + + +
+- Bitwarden ++ Vaultwarden +
+ form:nth-child(1) > div:nth-child(3) { ++ @extend %vw-hide; ++} ++ ++/* Hide the `This account is owned by a business` checkbox and label */ ++#ownedBusiness, ++label[for^="ownedBusiness"] { ++ @extend %vw-hide; ++} ++ ++/* Hide the radio button and label for the `Custom` org user type */ ++#userTypeCustom, ++label[for^="userTypeCustom"] { ++ @extend %vw-hide; ++} ++ ++/* Hide Business Name */ ++app-org-account form div bit-form-field.tw-block:nth-child(3) { ++ @extend %vw-hide; ++} ++ ++/* Hide organization plans */ ++app-organization-plans > form > bit-section:nth-child(2) { ++ @extend %vw-hide; ++} ++ ++/* Hide Device Verification form at the Two Step Login screen */ ++app-security > app-two-factor-setup > form { ++ @extend %vw-hide; ++} ++/**** END Vaultwarden CHANGES ****/ +diff --git a/apps/web/src/scss/tailwind.css b/apps/web/src/scss/tailwind.css +index 1ac7b15401..0c388f6787 100644 +--- a/apps/web/src/scss/tailwind.css ++++ b/apps/web/src/scss/tailwind.css +@@ -52,7 +52,7 @@ + img.new-logo-themed { + @apply tw-block; + +- width: 128px; ++ width: 175px; + } + .theme_light img.new-logo-themed { + content: url("../images/logo.svg"); +diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js +index 3ae0778250..868ee00eb0 100644 +--- a/apps/web/tailwind.config.js ++++ b/apps/web/tailwind.config.js +@@ -7,7 +7,6 @@ config.content = [ + "../../libs/auth/src/**/*.{html,ts}", + "../../libs/vault/src/**/*.{html,ts}", + "../../libs/angular/src/**/*.{html,ts}", +- "../../bitwarden_license/bit-web/src/**/*.{html,ts}", + ]; + + module.exports = config; +diff --git a/clients.code-workspace b/clients.code-workspace +index a424f91eeb..72f7c59185 100644 +--- a/clients.code-workspace ++++ b/clients.code-workspace +@@ -8,18 +8,10 @@ + "name": "web vault", + "path": "apps/web", + }, +- { +- "name": "web vault (bit)", +- "path": "bitwarden_license/bit-web", +- }, + { + "name": "cli", + "path": "apps/cli", + }, +- { +- "name": "cli (bit)", +- "path": "bitwarden_license/bit-cli", +- }, + { + "name": "desktop", + "path": "apps/desktop", +@@ -32,10 +24,6 @@ + "name": "libs", + "path": "libs", + }, +- { +- "name": "common (bit)", +- "path": "bitwarden_license/bit-common", +- }, + ], + "settings": { + "eslint.options": { +diff --git a/jest.config.js b/jest.config.js +index 829adf1bf7..8f4bd4b5db 100644 +--- a/jest.config.js ++++ b/jest.config.js +@@ -20,9 +20,6 @@ module.exports = { + "/apps/cli/jest.config.js", + "/apps/desktop/jest.config.js", + "/apps/web/jest.config.js", +- "/bitwarden_license/bit-web/jest.config.js", +- "/bitwarden_license/bit-cli/jest.config.js", +- "/bitwarden_license/bit-common/jest.config.js", + + "/libs/admin-console/jest.config.js", + "/libs/angular/jest.config.js", +diff --git a/libs/angular/src/auth/components/register.component.ts b/libs/angular/src/auth/components/register.component.ts +index 60adcba4d9..1406493881 100644 +--- a/libs/angular/src/auth/components/register.component.ts ++++ b/libs/angular/src/auth/components/register.component.ts +@@ -111,6 +111,14 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn + } + + async submit(showToast = true) { ++ if (typeof crypto.subtle === "undefined") { ++ this.platformUtilsService.showToast( ++ "error", ++ "This browser requires HTTPS to use the web vault", ++ "Check the Vaultwarden wiki for details on how to enable it", ++ ); ++ return; ++ } + let email = this.formGroup.value.email; + email = email.trim().toLowerCase(); + let name = this.formGroup.value.name; +diff --git a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts +index 2744633574..35918bf6f7 100644 +--- a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts ++++ b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts +@@ -6,6 +6,8 @@ import { AnonLayoutComponent } from "@bitwarden/auth/angular"; + import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + import { Icon, Translation } from "@bitwarden/components"; + ++import { BitwardenShield } from "../icons"; ++ + import { AnonLayoutWrapperDataService } from "./anon-layout-wrapper-data.service"; + + export interface AnonLayoutWrapperData { +@@ -148,7 +150,7 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy { + private resetPageData() { + this.pageTitle = null; + this.pageSubtitle = null; +- this.pageIcon = null; ++ this.pageIcon = BitwardenShield; + this.showReadonlyHostname = null; + this.maxWidth = null; + } +diff --git a/libs/auth/src/angular/anon-layout/anon-layout.component.html b/libs/auth/src/angular/anon-layout/anon-layout.component.html +index 8a0ac4b718..85d957a28d 100644 +--- a/libs/auth/src/angular/anon-layout/anon-layout.component.html ++++ b/libs/auth/src/angular/anon-layout/anon-layout.component.html +@@ -8,7 +8,7 @@ + 'tw-min-h-full': clientType === 'browser' || clientType === 'desktop', + }" + > +- ++ + + + +@@ -51,8 +51,14 @@ + + + +-
© {{ year }} Bitwarden Inc.
+-
{{ version }}
++
Vaultwarden Web
++
Version {{ version }}
++
++
++ A modified version of the Bitwarden® Web Vault for Vaultwarden (an unofficial rewrite of ++ the Bitwarden® server).
++ Vaultwarden is not associated with the Bitwarden® project nor Bitwarden Inc. ++
+
+ + +diff --git a/libs/components/src/tw-theme.css b/libs/components/src/tw-theme.css +index 7214ae73ef..83cbf9d162 100644 +--- a/libs/components/src/tw-theme.css ++++ b/libs/components/src/tw-theme.css +@@ -14,16 +14,16 @@ + --color-background: 255 255 255; + --color-background-alt: 251 251 251; + --color-background-alt2: 23 92 219; +- --color-background-alt3: 18 82 163; +- --color-background-alt4: 13 60 119; ++ --color-background-alt3: 33 37 41; /* bg of menu panel */ ++ --color-background-alt4: 16 18 21; /* bg of active menu item */ + + /* Can only be used behind the extension refresh flag */ + --color-primary-100: 200 217 249; +- --color-primary-300: 103 149 232; ++ --color-primary-300: 108 117 125; /* hover of menu items */ + /* Can only be used behind the extension refresh flag */ + --color-primary-500: 23 93 220; +- --color-primary-600: 23 93 220; +- --color-primary-700: 18 82 163; ++ --color-primary-600: 18 82 163; /* color of links and buttons */ ++ --color-primary-700: 13 60 119; /* hover of links and buttons */ + + --color-secondary-100: 240 240 240; + --color-secondary-300: 206 212 220; +@@ -52,7 +52,7 @@ + --color-text-code: 192 17 118; + --color-text-headers: 2 15 102; + +- --color-marketing-logo: 23 93 220; ++ --color-marketing-logo: 15 15 15; + + --tw-ring-offset-color: #ffffff; + } +diff --git a/libs/components/tailwind.config.js b/libs/components/tailwind.config.js +index 7a53c82ec5..9d0a337bd2 100644 +--- a/libs/components/tailwind.config.js ++++ b/libs/components/tailwind.config.js +@@ -6,7 +6,6 @@ config.content = [ + "libs/auth/src/**/*.{html,ts,mdx}", + "apps/web/src/**/*.{html,ts,mdx}", + "apps/browser/src/**/*.{html,ts,mdx}", +- "bitwarden_license/bit-web/src/**/*.{html,ts,mdx}", + ".storybook/preview.tsx", + ]; + config.safelist = [ +diff --git a/tailwind.config.js b/tailwind.config.js +index 637c28a54e..7afcea4e65 100644 +--- a/tailwind.config.js ++++ b/tailwind.config.js +@@ -10,7 +10,6 @@ config.content = [ + "./libs/tools/send/send-ui/src/*.{html,ts,mdx}", + "./libs/vault/src/**/*.{html,ts,mdx}", + "./apps/web/src/**/*.{html,ts,mdx}", +- "./bitwarden_license/bit-web/src/**/*.{html,ts,mdx}", + "./.storybook/preview.js", + ]; + config.safelist = [ +diff --git a/tsconfig.json b/tsconfig.json +index 225321663c..7a7dde454f 100644 +--- a/tsconfig.json ++++ b/tsconfig.json +@@ -37,8 +37,7 @@ + "@bitwarden/tools-card": ["./libs/tools/card/src"], + "@bitwarden/node/*": ["./libs/node/src/*"], + "@bitwarden/web-vault/*": ["./apps/web/src/*"], +- "@bitwarden/vault": ["./libs/vault/src"], +- "@bitwarden/bit-common/*": ["./bitwarden_license/bit-common/src/*"] ++ "@bitwarden/vault": ["./libs/vault/src"] + }, + "plugins": [ + { +@@ -52,9 +51,7 @@ + "apps/browser/src/**/*", + "libs/*/src/**/*", + "libs/tools/send/**/src/**/*", +- "libs/tools/card/src/**/*", +- "bitwarden_license/bit-web/src/**/*", +- "bitwarden_license/bit-common/src/**/*" ++ "libs/tools/card/src/**/*" + ], + "exclude": [ + "apps/web/src/**/*.spec.ts", diff --git a/patches/v2024.11.2.patch b/patches/v2024.11.2.patch new file mode 100644 index 0000000..9f764be --- /dev/null +++ b/patches/v2024.11.2.patch @@ -0,0 +1,796 @@ +diff --git a/apps/web/config/base.json b/apps/web/config/base.json +index cfaf604fb0..804252469a 100644 +--- a/apps/web/config/base.json ++++ b/apps/web/config/base.json +@@ -1,11 +1,5 @@ + { + "urls": {}, +- "stripeKey": "pk_test_KPoCfZXu7mznb9uSCPZ2JpTD", +- "braintreeKey": "sandbox_r72q8jq6_9pnxkwm75f87sdc2", +- "paypal": { +- "businessId": "AD3LAUZSNVPJY", +- "buttonAction": "https://www.sandbox.paypal.com/cgi-bin/webscr" +- }, + "dev": { + "port": 8080, + "allowedHosts": "auto" +diff --git a/apps/web/src/app/admin-console/organizations/create/organization-information.component.html b/apps/web/src/app/admin-console/organizations/create/organization-information.component.html +index e0a8006081..789efd9264 100644 +--- a/apps/web/src/app/admin-console/organizations/create/organization-information.component.html ++++ b/apps/web/src/app/admin-console/organizations/create/organization-information.component.html +@@ -12,7 +12,7 @@ + + + +- {{ "billingEmail" | i18n }} ++ {{ "email" | i18n }} + + + +diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts +index 0beb87c81c..d1e5961c2b 100644 +--- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts ++++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts +@@ -142,6 +142,7 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy { + } + + canShowBillingTab(organization: Organization): boolean { ++ return false; // disable billing tab in Vaultwarden + return canAccessBillingTab(organization); + } + +diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.ts b/apps/web/src/app/admin-console/organizations/members/members.component.ts +index 394c900f8d..7ef8fb741f 100644 +--- a/apps/web/src/app/admin-console/organizations/members/members.component.ts ++++ b/apps/web/src/app/admin-console/organizations/members/members.component.ts +@@ -193,11 +193,7 @@ export class MembersComponent extends BaseMembersComponent + .find((p) => p.organizationId === this.organization.id); + this.orgResetPasswordPolicyEnabled = resetPasswordPolicy?.enabled; + +- const billingMetadata = await this.billingApiService.getOrganizationBillingMetadata( +- this.organization.id, +- ); +- +- this.orgIsOnSecretsManagerStandalone = billingMetadata.isOnSecretsManagerStandalone; ++ this.orgIsOnSecretsManagerStandalone = false; // don't get billing metadata + + await this.load(); + +diff --git a/apps/web/src/app/admin-console/organizations/organization-routing.module.ts b/apps/web/src/app/admin-console/organizations/organization-routing.module.ts +index a36b267e2f..538cc45ac6 100644 +--- a/apps/web/src/app/admin-console/organizations/organization-routing.module.ts ++++ b/apps/web/src/app/admin-console/organizations/organization-routing.module.ts +@@ -62,13 +62,6 @@ const routes: Routes = [ + (m) => m.OrganizationReportingModule, + ), + }, +- { +- path: "access-intelligence", +- loadChildren: () => +- import("../../tools/access-intelligence/access-intelligence.module").then( +- (m) => m.AccessIntelligenceModule, +- ), +- }, + { + path: "billing", + loadChildren: () => +diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.html b/apps/web/src/app/admin-console/organizations/settings/account.component.html +index d1a1a09192..0df5c1533d 100644 +--- a/apps/web/src/app/admin-console/organizations/settings/account.component.html ++++ b/apps/web/src/app/admin-console/organizations/settings/account.component.html +@@ -16,7 +16,7 @@ + + + +- {{ "billingEmail" | i18n }} ++ {{ "email" | i18n }} + + +
+diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.ts b/apps/web/src/app/admin-console/organizations/settings/account.component.ts +index 2d97f95e51..820a01da50 100644 +--- a/apps/web/src/app/admin-console/organizations/settings/account.component.ts ++++ b/apps/web/src/app/admin-console/organizations/settings/account.component.ts +@@ -96,7 +96,7 @@ export class AccountComponent implements OnInit, OnDestroy { + ) {} + + async ngOnInit() { +- this.selfHosted = this.platformUtilsService.isSelfHost(); ++ this.selfHosted = false; // set to false so we can rename organizations + + this.configService + .getFeatureFlag$(FeatureFlag.LimitCollectionCreationDeletionSplit) +@@ -211,6 +211,7 @@ export class AccountComponent implements OnInit, OnDestroy { + }; + + submitCollectionManagement = async () => { ++ return; // flexible collections are not supported by Vaultwarden + // Early exit if self-hosted + if (this.selfHosted && !this.limitCollectionCreationDeletionSplitFeatureFlagIsEnabled) { + return; +diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts +index 704960c14c..0cc7bbf9c1 100644 +--- a/apps/web/src/app/app.component.ts ++++ b/apps/web/src/app/app.component.ts +@@ -231,6 +231,10 @@ export class AppComponent implements OnDestroy, OnInit { + break; + } + case "showToast": ++ if (typeof message.text === "string" && typeof crypto.subtle === "undefined") { ++ message.title = "This browser requires HTTPS to use the web vault"; ++ message.text = "Check the Vaultwarden wiki for details on how to enable it"; ++ } + this.toastService._showToast(message); + break; + case "convertAccountToKeyConnector": +diff --git a/apps/web/src/app/auth/settings/change-password.component.html b/apps/web/src/app/auth/settings/change-password.component.html +index 91144fdfc1..d3dc4410c9 100644 +--- a/apps/web/src/app/auth/settings/change-password.component.html ++++ b/apps/web/src/app/auth/settings/change-password.component.html +@@ -125,5 +125,3 @@ + {{ "changeMasterPassword" | i18n }} + + +- +- +diff --git a/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts b/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts +index da5378f479..6d80d96ff5 100644 +--- a/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts ++++ b/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts +@@ -187,11 +187,11 @@ export class TwoFactorAuthenticatorComponent + new window.QRious({ + element: document.getElementById("qr"), + value: +- "otpauth://totp/Bitwarden:" + ++ "otpauth://totp/Vaultwarden:" + + Utils.encodeRFC3986URIComponent(email) + + "?secret=" + + encodeURIComponent(this.key) + +- "&issuer=Bitwarden", ++ "&issuer=Vaultwarden", + size: 160, + }); + } +diff --git a/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts b/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts +index 30ae39d481..37e2db43d2 100644 +--- a/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts ++++ b/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts +@@ -52,6 +52,7 @@ export class OrgBillingHistoryViewComponent implements OnInit, OnDestroy { + + this.loading = true; + ++ /* disable billing history + const openInvoicesPromise = this.organizationBillingApiService.getBillingInvoices( + this.organizationId, + "open", +@@ -85,6 +86,7 @@ export class OrgBillingHistoryViewComponent implements OnInit, OnDestroy { + openInvoices.length <= pageSize || + paidInvoices.length <= pageSize || + transactions.length <= pageSize; ++ */ + + this.loading = false; + } +diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.html b/apps/web/src/app/billing/organizations/organization-plans.component.html +index e1b74abea7..aff83361b6 100644 +--- a/apps/web/src/app/billing/organizations/organization-plans.component.html ++++ b/apps/web/src/app/billing/organizations/organization-plans.component.html +@@ -6,7 +6,7 @@ + > + {{ "loading" | i18n }} + +- ++ + +

{{ "uploadLicenseFileOrg" | i18n }}

+
+@@ -40,12 +40,7 @@ + (onLicenseFileUploaded)="onLicenseFileUploaded($event)" + /> + +- ++ + + !!plan.PasswordManager); +@@ -216,6 +217,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + this.plan = providerDefaultPlan.type; + this.productTier = providerDefaultPlan.productTier; + } ++ end of asking /api/plans in Vaultwarden */ + + if (!this.createOrganization) { + this.upgradeFlowPrefillForm(); +@@ -290,6 +292,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + get selectableProducts() { ++ return null; // there are no products to select in Vaultwarden + if (this.acceptingSponsorship) { + const familyPlan = this.passwordManagerPlans.find( + (plan) => plan.type === PlanType.FamiliesAnnually, +@@ -321,6 +324,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + get selectablePlans() { ++ return null; // no plans to select in Vaultwarden + const selectedProductTierType = this.formGroup.controls.productTier.value; + const result = + this.passwordManagerPlans?.filter( +@@ -462,6 +466,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + get planOffersSecretsManager() { ++ return false; // no support for secrets manager in Vaultwarden + return this.selectedSecretsManagerPlan != null; + } + +@@ -470,6 +475,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + changedProduct() { ++ return; // no choice of products in Vaultwarden + const selectedPlan = this.selectablePlans[0]; + + this.setPlanType(selectedPlan.type); +@@ -605,11 +611,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + const collectionCt = collection.encryptedString; + const orgKeys = await this.keyService.makeKeyPair(orgKey[1]); + +- if (this.selfHosted) { +- orgId = await this.createSelfHosted(key, collectionCt, orgKeys); +- } else { +- orgId = await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]); +- } ++ // always use createCloudHosted() to disable license file upload ++ orgId = await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]); + + this.toastService.showToast({ + variant: "success", +@@ -715,7 +718,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + request.billingEmail = this.formGroup.controls.billingEmail.value; + request.initiationPath = "New organization creation in-product"; + request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString); ++ request.planType = PlanType.Free; // always select the free plan in Vaultwarden + ++ /* there is no plan to select in Vaultwarden + if (this.selectedPlan.type === PlanType.Free) { + request.planType = PlanType.Free; + } else { +@@ -749,6 +754,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + + // Secrets Manager + this.buildSecretsManagerRequest(request); ++ end plan selection and no support for secret manager in Vaultwarden */ + + if (this.hasProvider) { + const providerRequest = new ProviderOrganizationCreateRequest( +@@ -828,6 +834,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + private upgradeFlowPrefillForm() { ++ return; // Vaultwarden only supports free plan + if (this.acceptingSponsorship) { + this.formGroup.controls.productTier.setValue(ProductTierType.Families); + this.changedProduct(); +diff --git a/apps/web/src/app/core/router.service.ts b/apps/web/src/app/core/router.service.ts +index 0790162848..5728616cc8 100644 +--- a/apps/web/src/app/core/router.service.ts ++++ b/apps/web/src/app/core/router.service.ts +@@ -59,7 +59,7 @@ export class RouterService { + .subscribe((event: NavigationEnd) => { + this.currentUrl = event.url; + +- let title = i18nService.t("bitWebVault"); ++ let title = "Vaultwarden Web"; + + if (this.currentUrl.includes("/sm/")) { + title = i18nService.t("bitSecretsManager"); +diff --git a/apps/web/src/app/core/web-platform-utils.service.ts b/apps/web/src/app/core/web-platform-utils.service.ts +index dbd0ef593d..3452567592 100644 +--- a/apps/web/src/app/core/web-platform-utils.service.ts ++++ b/apps/web/src/app/core/web-platform-utils.service.ts +@@ -133,14 +133,17 @@ export class WebPlatformUtilsService implements PlatformUtilsService { + } + + isDev(): boolean { ++ return false; // treat Vaultwarden as production ready + return process.env.NODE_ENV === "development"; + } + + isSelfHost(): boolean { ++ return true; // treat Vaultwarden as self hosted + return WebPlatformUtilsService.isSelfHost(); + } + + static isSelfHost(): boolean { ++ return true; // treat Vaultwarden as self hosted + return process.env.ENV.toString() === "selfhosted"; + } + +diff --git a/apps/web/src/app/layouts/frontend-layout.component.html b/apps/web/src/app/layouts/frontend-layout.component.html +index 72f0f1f1da..cea0867131 100644 +--- a/apps/web/src/app/layouts/frontend-layout.component.html ++++ b/apps/web/src/app/layouts/frontend-layout.component.html +@@ -1,6 +1,11 @@ + +
+- +- © {{ year }} Bitwarden Inc.
++ Vaultwarden Web
+ {{ "versionNumber" | i18n: version }} ++

++
++ A modified version of the Bitwarden® Web Vault for Vaultwarden (an unofficial rewrite of the ++ Bitwarden® server).
++ Vaultwarden is not associated with the Bitwarden® project nor Bitwarden Inc. ++
+
+diff --git a/apps/web/src/app/layouts/header/web-header.component.html b/apps/web/src/app/layouts/header/web-header.component.html +index 7cba19b29a..024b14daad 100644 +--- a/apps/web/src/app/layouts/header/web-header.component.html ++++ b/apps/web/src/app/layouts/header/web-header.component.html +@@ -61,7 +61,12 @@ + + {{ "accountSettings" | i18n }} + +- ++ + + {{ "getHelp" | i18n }} + +diff --git a/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html b/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html +index 08195d95bf..f51a216a14 100644 +--- a/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html ++++ b/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html +@@ -10,10 +10,7 @@ + > + + +-
++
+ {{ "moreFromBitwarden" | i18n }} + + +diff --git a/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html b/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html +index 41346675bb..d366fdf7d7 100644 +--- a/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html ++++ b/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html +@@ -30,7 +30,7 @@ + + +
+ {{ "moreFromBitwarden" | i18n }} +diff --git a/apps/web/src/app/layouts/user-layout.component.ts b/apps/web/src/app/layouts/user-layout.component.ts +index bd02533233..072465f7af 100644 +--- a/apps/web/src/app/layouts/user-layout.component.ts ++++ b/apps/web/src/app/layouts/user-layout.component.ts +@@ -1,7 +1,7 @@ + import { CommonModule } from "@angular/common"; + import { Component, OnInit } from "@angular/core"; + import { RouterModule } from "@angular/router"; +-import { Observable, combineLatest, concatMap } from "rxjs"; ++import { Observable, of } from "rxjs"; + + import { JslibModule } from "@bitwarden/angular/jslib.module"; + import { ApiService } from "@bitwarden/common/abstractions/api.service"; +@@ -38,26 +38,7 @@ export class UserLayoutComponent implements OnInit { + + await this.syncService.fullSync(false); + +- this.hasFamilySponsorshipAvailable$ = this.organizationService.canManageSponsorships$; +- +- // We want to hide the subscription menu for organizations that provide premium. +- // Except if the user has premium personally or has a billing history. +- this.showSubscription$ = combineLatest([ +- this.billingAccountProfileStateService.hasPremiumPersonally$, +- this.billingAccountProfileStateService.hasPremiumFromAnyOrganization$, +- ]).pipe( +- concatMap(async ([hasPremiumPersonally, hasPremiumFromOrg]) => { +- const isCloud = !this.platformUtilsService.isSelfHost(); +- +- let billing = null; +- if (isCloud) { +- // TODO: We should remove the need to call this! +- billing = await this.apiService.getUserBillingHistory(); +- } +- +- const cloudAndBillingHistory = isCloud && !billing?.hasNoHistory; +- return hasPremiumPersonally || !hasPremiumFromOrg || cloudAndBillingHistory; +- }), +- ); ++ this.hasFamilySponsorshipAvailable$ = of(false); // disable family Sponsorships in Vaultwarden ++ this.showSubscription$ = of(false); // always hide subscriptions in Vaultwarden + } + } +diff --git a/apps/web/src/app/platform/web-environment.service.ts b/apps/web/src/app/platform/web-environment.service.ts +index ebddc7491b..7e12389a4a 100644 +--- a/apps/web/src/app/platform/web-environment.service.ts ++++ b/apps/web/src/app/platform/web-environment.service.ts +@@ -36,7 +36,17 @@ export class WebEnvironmentService extends DefaultEnvironmentService { + super(stateProvider, accountService, additionalRegionConfigs); + + // The web vault always uses the current location as the base url +- envUrls.base ??= this.win.location.origin; ++ // If the base URL is `https://vaultwarden.example.com/base/path/`, ++ // `window.location.href` should have one of the following forms: ++ // ++ // - `https://vaultwarden.example.com/base/path/` ++ // - `https://vaultwarden.example.com/base/path/#/some/route[?queryParam=...]` ++ // - `https://vaultwarden.example.com/base/path/?queryParam=...` ++ // ++ // We want to get to just `https://vaultwarden.example.com/base/path`. ++ let baseUrl = this.win.location.href; ++ baseUrl = baseUrl.replace(/(\/+|\/*#.*|\/*\?.*)$/, ""); // Strip off trailing `/`, `#`, `?` and everything after. ++ envUrls.base ??= baseUrl; + + // Find the region + const currentHostname = new URL(this.win.location.href).hostname; +diff --git a/apps/web/src/app/tools/send/add-edit.component.html b/apps/web/src/app/tools/send/add-edit.component.html +index 7eade18a7c..b8f865faa7 100644 +--- a/apps/web/src/app/tools/send/add-edit.component.html ++++ b/apps/web/src/app/tools/send/add-edit.component.html +@@ -230,7 +230,12 @@ + {{ "password" | i18n }} + {{ "newPassword" | i18n }} + +- ++ + + {{ "sendPasswordDesc" | i18n }} + +diff --git a/apps/web/src/app/tools/send/send-access-explainer.component.html b/apps/web/src/app/tools/send/send-access-explainer.component.html +index e8090cb850..284b916514 100644 +--- a/apps/web/src/app/tools/send/send-access-explainer.component.html ++++ b/apps/web/src/app/tools/send/send-access-explainer.component.html +@@ -1,18 +1,5 @@ +
+

+ {{ "sendAccessTaglineProductDesc" | i18n }} +- {{ "sendAccessTaglineLearnMore" | i18n }} +- Bitwarden Send +- {{ "sendAccessTaglineOr" | i18n }} +- {{ +- "sendAccessTaglineSignUp" | i18n +- }} +- {{ "sendAccessTaglineTryToday" | i18n }} +

+
+diff --git a/apps/web/src/index.html b/apps/web/src/index.html +index ce1a955b88..41127c4d40 100644 +--- a/apps/web/src/index.html ++++ b/apps/web/src/index.html +@@ -1,22 +1,24 @@ +- ++ + + + + + + +- Bitwarden Web vault ++ Vaultwarden Web + + + + + + ++ ++ + + + +
+- Bitwarden ++ Vaultwarden +
+ /apps/cli/jest.config.js", + "/apps/desktop/jest.config.js", + "/apps/web/jest.config.js", +- "/bitwarden_license/bit-web/jest.config.js", +- "/bitwarden_license/bit-cli/jest.config.js", +- "/bitwarden_license/bit-common/jest.config.js", + + "/libs/admin-console/jest.config.js", + "/libs/angular/jest.config.js", +diff --git a/libs/angular/src/auth/components/register.component.ts b/libs/angular/src/auth/components/register.component.ts +index 94f60ff637..c02eaeab70 100644 +--- a/libs/angular/src/auth/components/register.component.ts ++++ b/libs/angular/src/auth/components/register.component.ts +@@ -111,6 +111,14 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn + } + + async submit(showToast = true) { ++ if (typeof crypto.subtle === "undefined") { ++ this.platformUtilsService.showToast( ++ "error", ++ "This browser requires HTTPS to use the web vault", ++ "Check the Vaultwarden wiki for details on how to enable it", ++ ); ++ return; ++ } + let email = this.formGroup.value.email; + email = email.trim().toLowerCase(); + let name = this.formGroup.value.name; +diff --git a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts +index f805da0700..c720671812 100644 +--- a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts ++++ b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts +@@ -6,6 +6,8 @@ import { AnonLayoutComponent } from "@bitwarden/auth/angular"; + import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + import { Icon, Translation } from "@bitwarden/components"; + ++import { BitwardenShield } from "../icons"; ++ + import { AnonLayoutWrapperDataService } from "./anon-layout-wrapper-data.service"; + + export interface AnonLayoutWrapperData { +@@ -152,7 +154,7 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy { + private resetPageData() { + this.pageTitle = null; + this.pageSubtitle = null; +- this.pageIcon = null; ++ this.pageIcon = BitwardenShield; + this.showReadonlyHostname = null; + this.maxWidth = null; + } +diff --git a/libs/auth/src/angular/anon-layout/anon-layout.component.html b/libs/auth/src/angular/anon-layout/anon-layout.component.html +index 8a0ac4b718..85d957a28d 100644 +--- a/libs/auth/src/angular/anon-layout/anon-layout.component.html ++++ b/libs/auth/src/angular/anon-layout/anon-layout.component.html +@@ -8,7 +8,7 @@ + 'tw-min-h-full': clientType === 'browser' || clientType === 'desktop', + }" + > +- ++ + + + +@@ -51,8 +51,14 @@ + + + +-
© {{ year }} Bitwarden Inc.
+-
{{ version }}
++
Vaultwarden Web
++
Version {{ version }}
++
++
++ A modified version of the Bitwarden® Web Vault for Vaultwarden (an unofficial rewrite of ++ the Bitwarden® server).
++ Vaultwarden is not associated with the Bitwarden® project nor Bitwarden Inc. ++
+
+ + +diff --git a/libs/components/src/tw-theme.css b/libs/components/src/tw-theme.css +index 7214ae73ef..83cbf9d162 100644 +--- a/libs/components/src/tw-theme.css ++++ b/libs/components/src/tw-theme.css +@@ -14,16 +14,16 @@ + --color-background: 255 255 255; + --color-background-alt: 251 251 251; + --color-background-alt2: 23 92 219; +- --color-background-alt3: 18 82 163; +- --color-background-alt4: 13 60 119; ++ --color-background-alt3: 33 37 41; /* bg of menu panel */ ++ --color-background-alt4: 16 18 21; /* bg of active menu item */ + + /* Can only be used behind the extension refresh flag */ + --color-primary-100: 200 217 249; +- --color-primary-300: 103 149 232; ++ --color-primary-300: 108 117 125; /* hover of menu items */ + /* Can only be used behind the extension refresh flag */ + --color-primary-500: 23 93 220; +- --color-primary-600: 23 93 220; +- --color-primary-700: 18 82 163; ++ --color-primary-600: 18 82 163; /* color of links and buttons */ ++ --color-primary-700: 13 60 119; /* hover of links and buttons */ + + --color-secondary-100: 240 240 240; + --color-secondary-300: 206 212 220; +@@ -52,7 +52,7 @@ + --color-text-code: 192 17 118; + --color-text-headers: 2 15 102; + +- --color-marketing-logo: 23 93 220; ++ --color-marketing-logo: 15 15 15; + + --tw-ring-offset-color: #ffffff; + } +diff --git a/libs/components/tailwind.config.js b/libs/components/tailwind.config.js +index 7a53c82ec5..9d0a337bd2 100644 +--- a/libs/components/tailwind.config.js ++++ b/libs/components/tailwind.config.js +@@ -6,7 +6,6 @@ config.content = [ + "libs/auth/src/**/*.{html,ts,mdx}", + "apps/web/src/**/*.{html,ts,mdx}", + "apps/browser/src/**/*.{html,ts,mdx}", +- "bitwarden_license/bit-web/src/**/*.{html,ts,mdx}", + ".storybook/preview.tsx", + ]; + config.safelist = [ +diff --git a/tailwind.config.js b/tailwind.config.js +index 637c28a54e..7afcea4e65 100644 +--- a/tailwind.config.js ++++ b/tailwind.config.js +@@ -10,7 +10,6 @@ config.content = [ + "./libs/tools/send/send-ui/src/*.{html,ts,mdx}", + "./libs/vault/src/**/*.{html,ts,mdx}", + "./apps/web/src/**/*.{html,ts,mdx}", +- "./bitwarden_license/bit-web/src/**/*.{html,ts,mdx}", + "./.storybook/preview.js", + ]; + config.safelist = [ +diff --git a/tsconfig.json b/tsconfig.json +index 225321663c..7a7dde454f 100644 +--- a/tsconfig.json ++++ b/tsconfig.json +@@ -37,8 +37,7 @@ + "@bitwarden/tools-card": ["./libs/tools/card/src"], + "@bitwarden/node/*": ["./libs/node/src/*"], + "@bitwarden/web-vault/*": ["./apps/web/src/*"], +- "@bitwarden/vault": ["./libs/vault/src"], +- "@bitwarden/bit-common/*": ["./bitwarden_license/bit-common/src/*"] ++ "@bitwarden/vault": ["./libs/vault/src"] + }, + "plugins": [ + { +@@ -52,9 +51,7 @@ + "apps/browser/src/**/*", + "libs/*/src/**/*", + "libs/tools/send/**/src/**/*", +- "libs/tools/card/src/**/*", +- "bitwarden_license/bit-web/src/**/*", +- "bitwarden_license/bit-common/src/**/*" ++ "libs/tools/card/src/**/*" + ], + "exclude": [ + "apps/web/src/**/*.spec.ts", diff --git a/patches/v2024.12.0.patch b/patches/v2024.12.0.patch new file mode 100644 index 0000000..5282c17 --- /dev/null +++ b/patches/v2024.12.0.patch @@ -0,0 +1,783 @@ +diff --git a/apps/web/config/base.json b/apps/web/config/base.json +index e64ef6ebeb..b41ca4219d 100644 +--- a/apps/web/config/base.json ++++ b/apps/web/config/base.json +@@ -1,11 +1,5 @@ + { + "urls": {}, +- "stripeKey": "pk_test_KPoCfZXu7mznb9uSCPZ2JpTD", +- "braintreeKey": "sandbox_r72q8jq6_9pnxkwm75f87sdc2", +- "paypal": { +- "businessId": "AD3LAUZSNVPJY", +- "buttonAction": "https://www.sandbox.paypal.com/cgi-bin/webscr" +- }, + "dev": { + "port": 8080, + "allowedHosts": "auto" +diff --git a/apps/web/src/app/admin-console/organizations/create/organization-information.component.html b/apps/web/src/app/admin-console/organizations/create/organization-information.component.html +index e0a8006081..789efd9264 100644 +--- a/apps/web/src/app/admin-console/organizations/create/organization-information.component.html ++++ b/apps/web/src/app/admin-console/organizations/create/organization-information.component.html +@@ -12,7 +12,7 @@ + + + +- {{ "billingEmail" | i18n }} ++ {{ "email" | i18n }} + + + +diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts +index a15882f42d..39958cb881 100644 +--- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts ++++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts +@@ -127,6 +127,7 @@ export class OrganizationLayoutComponent implements OnInit { + } + + canShowBillingTab(organization: Organization): boolean { ++ return false; // disable billing tab in Vaultwarden + return canAccessBillingTab(organization); + } + +diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.ts b/apps/web/src/app/admin-console/organizations/members/members.component.ts +index 3e61ec2a20..445f813d17 100644 +--- a/apps/web/src/app/admin-console/organizations/members/members.component.ts ++++ b/apps/web/src/app/admin-console/organizations/members/members.component.ts +@@ -191,11 +191,7 @@ export class MembersComponent extends BaseMembersComponent + .find((p) => p.organizationId === this.organization.id); + this.orgResetPasswordPolicyEnabled = resetPasswordPolicy?.enabled; + +- const billingMetadata = await this.billingApiService.getOrganizationBillingMetadata( +- this.organization.id, +- ); +- +- this.orgIsOnSecretsManagerStandalone = billingMetadata.isOnSecretsManagerStandalone; ++ this.orgIsOnSecretsManagerStandalone = false; // don't get billing metadata + + await this.load(); + +diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.html b/apps/web/src/app/admin-console/organizations/settings/account.component.html +index b3fac56c0b..796a1a817a 100644 +--- a/apps/web/src/app/admin-console/organizations/settings/account.component.html ++++ b/apps/web/src/app/admin-console/organizations/settings/account.component.html +@@ -16,7 +16,7 @@ + + + +- {{ "billingEmail" | i18n }} ++ {{ "email" | i18n }} + + +
+diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.ts b/apps/web/src/app/admin-console/organizations/settings/account.component.ts +index 5182406564..859a7d86f0 100644 +--- a/apps/web/src/app/admin-console/organizations/settings/account.component.ts ++++ b/apps/web/src/app/admin-console/organizations/settings/account.component.ts +@@ -84,7 +84,7 @@ export class AccountComponent implements OnInit, OnDestroy { + ) {} + + async ngOnInit() { +- this.selfHosted = this.platformUtilsService.isSelfHost(); ++ this.selfHosted = false; // set to false so we can rename organizations + + this.route.params + .pipe( +@@ -177,6 +177,7 @@ export class AccountComponent implements OnInit, OnDestroy { + }; + + submitCollectionManagement = async () => { ++ return; // flexible collections are not supported by Vaultwarden + const request = new OrganizationCollectionManagementUpdateRequest(); + request.limitCollectionCreation = + this.collectionManagementFormGroup.value.limitCollectionCreation; +diff --git a/apps/web/src/app/app.component.html b/apps/web/src/app/app.component.html +index a39c045e0e..9d72e609b4 100644 +--- a/apps/web/src/app/app.component.html ++++ b/apps/web/src/app/app.component.html +@@ -3,7 +3,7 @@ + when the body has the layout_frontend class. Having this match the index allows for a duplicative yet seamless + loading state here for process reloading. --> +
+- Bitwarden ++ Vaultwarden +
+ + +- +- +diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts +index a018710391..c37847a474 100644 +--- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts ++++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.ts +@@ -187,11 +187,11 @@ export class TwoFactorSetupAuthenticatorComponent + new window.QRious({ + element: document.getElementById("qr"), + value: +- "otpauth://totp/Bitwarden:" + ++ "otpauth://totp/Vaultwarden:" + + Utils.encodeRFC3986URIComponent(email) + + "?secret=" + + encodeURIComponent(this.key) + +- "&issuer=Bitwarden", ++ "&issuer=Vaultwarden", + size: 160, + }); + } +diff --git a/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts b/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts +index 30ae39d481..37e2db43d2 100644 +--- a/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts ++++ b/apps/web/src/app/billing/organizations/organization-billing-history-view.component.ts +@@ -52,6 +52,7 @@ export class OrgBillingHistoryViewComponent implements OnInit, OnDestroy { + + this.loading = true; + ++ /* disable billing history + const openInvoicesPromise = this.organizationBillingApiService.getBillingInvoices( + this.organizationId, + "open", +@@ -85,6 +86,7 @@ export class OrgBillingHistoryViewComponent implements OnInit, OnDestroy { + openInvoices.length <= pageSize || + paidInvoices.length <= pageSize || + transactions.length <= pageSize; ++ */ + + this.loading = false; + } +diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.html b/apps/web/src/app/billing/organizations/organization-plans.component.html +index e1b74abea7..aff83361b6 100644 +--- a/apps/web/src/app/billing/organizations/organization-plans.component.html ++++ b/apps/web/src/app/billing/organizations/organization-plans.component.html +@@ -6,7 +6,7 @@ + > + {{ "loading" | i18n }} + +- ++ + +

{{ "uploadLicenseFileOrg" | i18n }}

+
+@@ -40,12 +40,7 @@ + (onLicenseFileUploaded)="onLicenseFileUploaded($event)" + /> + +- ++ + + !!plan.PasswordManager); +@@ -216,6 +217,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + this.plan = providerDefaultPlan.type; + this.productTier = providerDefaultPlan.productTier; + } ++ end of asking /api/plans in Vaultwarden */ + + if (!this.createOrganization) { + this.upgradeFlowPrefillForm(); +@@ -290,6 +292,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + get selectableProducts() { ++ return null; // there are no products to select in Vaultwarden + if (this.acceptingSponsorship) { + const familyPlan = this.passwordManagerPlans.find( + (plan) => plan.type === PlanType.FamiliesAnnually, +@@ -321,6 +324,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + get selectablePlans() { ++ return null; // no plans to select in Vaultwarden + const selectedProductTierType = this.formGroup.controls.productTier.value; + const result = + this.passwordManagerPlans?.filter( +@@ -462,6 +466,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + get planOffersSecretsManager() { ++ return false; // no support for secrets manager in Vaultwarden + return this.selectedSecretsManagerPlan != null; + } + +@@ -470,6 +475,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + changedProduct() { ++ return; // no choice of products in Vaultwarden + const selectedPlan = this.selectablePlans[0]; + + this.setPlanType(selectedPlan.type); +@@ -605,11 +611,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + const collectionCt = collection.encryptedString; + const orgKeys = await this.keyService.makeKeyPair(orgKey[1]); + +- if (this.selfHosted) { +- orgId = await this.createSelfHosted(key, collectionCt, orgKeys); +- } else { +- orgId = await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]); +- } ++ // always use createCloudHosted() to disable license file upload ++ orgId = await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]); + + this.toastService.showToast({ + variant: "success", +@@ -715,7 +718,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + request.billingEmail = this.formGroup.controls.billingEmail.value; + request.initiationPath = "New organization creation in-product"; + request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString); ++ request.planType = PlanType.Free; // always select the free plan in Vaultwarden + ++ /* there is no plan to select in Vaultwarden + if (this.selectedPlan.type === PlanType.Free) { + request.planType = PlanType.Free; + } else { +@@ -749,6 +754,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + + // Secrets Manager + this.buildSecretsManagerRequest(request); ++ end plan selection and no support for secret manager in Vaultwarden */ + + if (this.hasProvider) { + const providerRequest = new ProviderOrganizationCreateRequest( +@@ -828,6 +834,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { + } + + private upgradeFlowPrefillForm() { ++ return; // Vaultwarden only supports free plan + if (this.acceptingSponsorship) { + this.formGroup.controls.productTier.setValue(ProductTierType.Families); + this.changedProduct(); +diff --git a/apps/web/src/app/core/router.service.ts b/apps/web/src/app/core/router.service.ts +index 0790162848..5728616cc8 100644 +--- a/apps/web/src/app/core/router.service.ts ++++ b/apps/web/src/app/core/router.service.ts +@@ -59,7 +59,7 @@ export class RouterService { + .subscribe((event: NavigationEnd) => { + this.currentUrl = event.url; + +- let title = i18nService.t("bitWebVault"); ++ let title = "Vaultwarden Web"; + + if (this.currentUrl.includes("/sm/")) { + title = i18nService.t("bitSecretsManager"); +diff --git a/apps/web/src/app/core/web-platform-utils.service.ts b/apps/web/src/app/core/web-platform-utils.service.ts +index dbd0ef593d..3452567592 100644 +--- a/apps/web/src/app/core/web-platform-utils.service.ts ++++ b/apps/web/src/app/core/web-platform-utils.service.ts +@@ -133,14 +133,17 @@ export class WebPlatformUtilsService implements PlatformUtilsService { + } + + isDev(): boolean { ++ return false; // treat Vaultwarden as production ready + return process.env.NODE_ENV === "development"; + } + + isSelfHost(): boolean { ++ return true; // treat Vaultwarden as self hosted + return WebPlatformUtilsService.isSelfHost(); + } + + static isSelfHost(): boolean { ++ return true; // treat Vaultwarden as self hosted + return process.env.ENV.toString() === "selfhosted"; + } + +diff --git a/apps/web/src/app/layouts/frontend-layout.component.html b/apps/web/src/app/layouts/frontend-layout.component.html +index 72f0f1f1da..cea0867131 100644 +--- a/apps/web/src/app/layouts/frontend-layout.component.html ++++ b/apps/web/src/app/layouts/frontend-layout.component.html +@@ -1,6 +1,11 @@ + +
+- +- © {{ year }} Bitwarden Inc.
++ Vaultwarden Web
+ {{ "versionNumber" | i18n: version }} ++

++
++ A modified version of the Bitwarden® Web Vault for Vaultwarden (an unofficial rewrite of the ++ Bitwarden® server).
++ Vaultwarden is not associated with the Bitwarden® project nor Bitwarden Inc. ++
+
+diff --git a/apps/web/src/app/layouts/header/web-header.component.html b/apps/web/src/app/layouts/header/web-header.component.html +index 7cba19b29a..024b14daad 100644 +--- a/apps/web/src/app/layouts/header/web-header.component.html ++++ b/apps/web/src/app/layouts/header/web-header.component.html +@@ -61,7 +61,12 @@ + + {{ "accountSettings" | i18n }} + +- ++ + + {{ "getHelp" | i18n }} + +diff --git a/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html b/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html +index 08195d95bf..f51a216a14 100644 +--- a/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html ++++ b/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.html +@@ -10,10 +10,7 @@ + > + + +-
++
+ {{ "moreFromBitwarden" | i18n }} + + +diff --git a/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html b/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html +index 41346675bb..d366fdf7d7 100644 +--- a/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html ++++ b/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.html +@@ -30,7 +30,7 @@ + + +
+ {{ "moreFromBitwarden" | i18n }} +diff --git a/apps/web/src/app/layouts/user-layout.component.ts b/apps/web/src/app/layouts/user-layout.component.ts +index 88096c9b95..515f1149e6 100644 +--- a/apps/web/src/app/layouts/user-layout.component.ts ++++ b/apps/web/src/app/layouts/user-layout.component.ts +@@ -1,7 +1,7 @@ + import { CommonModule } from "@angular/common"; + import { Component, OnInit } from "@angular/core"; + import { RouterModule } from "@angular/router"; +-import { Observable, concatMap, combineLatest } from "rxjs"; ++import { Observable, of } from "rxjs"; + + import { JslibModule } from "@bitwarden/angular/jslib.module"; + import { ApiService } from "@bitwarden/common/abstractions/api.service"; +@@ -47,24 +47,8 @@ export class UserLayoutComponent implements OnInit { + + await this.syncService.fullSync(false); + +- // We want to hide the subscription menu for organizations that provide premium. +- // Except if the user has premium personally or has a billing history. +- this.showSubscription$ = combineLatest([ +- this.billingAccountProfileStateService.hasPremiumPersonally$, +- this.billingAccountProfileStateService.hasPremiumFromAnyOrganization$, +- ]).pipe( +- concatMap(async ([hasPremiumPersonally, hasPremiumFromOrg]) => { +- const isCloud = !this.platformUtilsService.isSelfHost(); +- +- let billing = null; +- if (isCloud) { +- // TODO: We should remove the need to call this! +- billing = await this.apiService.getUserBillingHistory(); +- } +- +- const cloudAndBillingHistory = isCloud && !billing?.hasNoHistory; +- return hasPremiumPersonally || !hasPremiumFromOrg || cloudAndBillingHistory; +- }), +- ); ++ this.hasFamilySponsorshipAvailable$ = of(false); // disable family Sponsorships in Vaultwarden ++ this.showSponsoredFamilies$ = of(false); // disable family Sponsorships in Vaultwarden ++ this.showSubscription$ = of(false); // always hide subscriptions in Vaultwarden + } + } +diff --git a/apps/web/src/app/platform/web-environment.service.ts b/apps/web/src/app/platform/web-environment.service.ts +index ebddc7491b..7e12389a4a 100644 +--- a/apps/web/src/app/platform/web-environment.service.ts ++++ b/apps/web/src/app/platform/web-environment.service.ts +@@ -36,7 +36,17 @@ export class WebEnvironmentService extends DefaultEnvironmentService { + super(stateProvider, accountService, additionalRegionConfigs); + + // The web vault always uses the current location as the base url +- envUrls.base ??= this.win.location.origin; ++ // If the base URL is `https://vaultwarden.example.com/base/path/`, ++ // `window.location.href` should have one of the following forms: ++ // ++ // - `https://vaultwarden.example.com/base/path/` ++ // - `https://vaultwarden.example.com/base/path/#/some/route[?queryParam=...]` ++ // - `https://vaultwarden.example.com/base/path/?queryParam=...` ++ // ++ // We want to get to just `https://vaultwarden.example.com/base/path`. ++ let baseUrl = this.win.location.href; ++ baseUrl = baseUrl.replace(/(\/+|\/*#.*|\/*\?.*)$/, ""); // Strip off trailing `/`, `#`, `?` and everything after. ++ envUrls.base ??= baseUrl; + + // Find the region + const currentHostname = new URL(this.win.location.href).hostname; +diff --git a/apps/web/src/app/tools/send/add-edit.component.html b/apps/web/src/app/tools/send/add-edit.component.html +index 7eade18a7c..b8f865faa7 100644 +--- a/apps/web/src/app/tools/send/add-edit.component.html ++++ b/apps/web/src/app/tools/send/add-edit.component.html +@@ -230,7 +230,12 @@ + {{ "password" | i18n }} + {{ "newPassword" | i18n }} + +- ++ + + {{ "sendPasswordDesc" | i18n }} + +diff --git a/apps/web/src/app/tools/send/send-access-explainer.component.html b/apps/web/src/app/tools/send/send-access-explainer.component.html +index e8090cb850..284b916514 100644 +--- a/apps/web/src/app/tools/send/send-access-explainer.component.html ++++ b/apps/web/src/app/tools/send/send-access-explainer.component.html +@@ -1,18 +1,5 @@ +
+

+ {{ "sendAccessTaglineProductDesc" | i18n }} +- {{ "sendAccessTaglineLearnMore" | i18n }} +- Bitwarden Send +- {{ "sendAccessTaglineOr" | i18n }} +- {{ +- "sendAccessTaglineSignUp" | i18n +- }} +- {{ "sendAccessTaglineTryToday" | i18n }} +

+
+diff --git a/apps/web/src/index.html b/apps/web/src/index.html +index 0b8ea86491..5d98411f46 100644 +--- a/apps/web/src/index.html ++++ b/apps/web/src/index.html +@@ -5,19 +5,21 @@ + + + +- Bitwarden Web vault ++ Vaultwarden Web + + + + + + ++ ++ + + + + +
+- Bitwarden ++ Vaultwarden +
+ /apps/cli/jest.config.js", + "/apps/desktop/jest.config.js", + "/apps/web/jest.config.js", +- "/bitwarden_license/bit-web/jest.config.js", +- "/bitwarden_license/bit-cli/jest.config.js", +- "/bitwarden_license/bit-common/jest.config.js", + + "/libs/admin-console/jest.config.js", + "/libs/angular/jest.config.js", +diff --git a/libs/angular/src/auth/components/register.component.ts b/libs/angular/src/auth/components/register.component.ts +index ec78a1692a..ce2539585a 100644 +--- a/libs/angular/src/auth/components/register.component.ts ++++ b/libs/angular/src/auth/components/register.component.ts +@@ -110,6 +110,14 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn + } + + async submit(showToast = true) { ++ if (typeof crypto.subtle === "undefined") { ++ this.platformUtilsService.showToast( ++ "error", ++ "This browser requires HTTPS to use the web vault", ++ "Check the Vaultwarden wiki for details on how to enable it", ++ ); ++ return; ++ } + let email = this.formGroup.value.email; + email = email.trim().toLowerCase(); + let name = this.formGroup.value.name; +diff --git a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts +index f805da0700..c720671812 100644 +--- a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts ++++ b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts +@@ -6,6 +6,8 @@ import { AnonLayoutComponent } from "@bitwarden/auth/angular"; + import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + import { Icon, Translation } from "@bitwarden/components"; + ++import { BitwardenShield } from "../icons"; ++ + import { AnonLayoutWrapperDataService } from "./anon-layout-wrapper-data.service"; + + export interface AnonLayoutWrapperData { +@@ -152,7 +154,7 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy { + private resetPageData() { + this.pageTitle = null; + this.pageSubtitle = null; +- this.pageIcon = null; ++ this.pageIcon = BitwardenShield; + this.showReadonlyHostname = null; + this.maxWidth = null; + } +diff --git a/libs/auth/src/angular/anon-layout/anon-layout.component.html b/libs/auth/src/angular/anon-layout/anon-layout.component.html +index 3323b6eca0..7be291e7f7 100644 +--- a/libs/auth/src/angular/anon-layout/anon-layout.component.html ++++ b/libs/auth/src/angular/anon-layout/anon-layout.component.html +@@ -8,7 +8,7 @@ + + + +@@ -52,8 +52,14 @@ + + + +-
© {{ year }} Bitwarden Inc.
+-
{{ version }}
++
Vaultwarden Web
++
Version {{ version }}
++
++
++ A modified version of the Bitwarden® Web Vault for Vaultwarden (an unofficial rewrite of ++ the Bitwarden® server).
++ Vaultwarden is not associated with the Bitwarden® project nor Bitwarden Inc. ++
+
+ + +diff --git a/libs/components/src/tw-theme.css b/libs/components/src/tw-theme.css +index 0a5a66337a..af5f0fb57a 100644 +--- a/libs/components/src/tw-theme.css ++++ b/libs/components/src/tw-theme.css +@@ -7,13 +7,13 @@ + --color-background: 255 255 255; + --color-background-alt: 243 246 249; + --color-background-alt2: 23 92 219; +- --color-background-alt3: 26 65 172; +- --color-background-alt4: 2 15 102; ++ --color-background-alt3: 33 37 41; /* bg of menu panel */ ++ --color-background-alt4: 16 18 21; /* bg of active menu item */ + + --color-primary-100: 219 229 246; +- --color-primary-300: 121 161 233; +- --color-primary-600: 23 93 220; +- --color-primary-700: 26 65 172; ++ --color-primary-300: 108 117 125; /* hover of menu items */ ++ --color-primary-600: 18 82 163; /* color of links and buttons */ ++ --color-primary-700: 13 60 119; /* hover of links and buttons */ + + --color-secondary-100: 230 233 239; + --color-secondary-300: 168 179 200; +@@ -49,7 +49,7 @@ + --color-text-alt2: 255 255 255; + --color-text-code: 192 17 118; + +- --color-marketing-logo: 23 93 220; ++ --color-marketing-logo: 15 15 15; /* The Vaultwarden Logo */ + + --tw-ring-offset-color: #ffffff; + } +diff --git a/libs/components/tailwind.config.js b/libs/components/tailwind.config.js +index 7a53c82ec5..9d0a337bd2 100644 +--- a/libs/components/tailwind.config.js ++++ b/libs/components/tailwind.config.js +@@ -6,7 +6,6 @@ config.content = [ + "libs/auth/src/**/*.{html,ts,mdx}", + "apps/web/src/**/*.{html,ts,mdx}", + "apps/browser/src/**/*.{html,ts,mdx}", +- "bitwarden_license/bit-web/src/**/*.{html,ts,mdx}", + ".storybook/preview.tsx", + ]; + config.safelist = [ +diff --git a/tailwind.config.js b/tailwind.config.js +index 637c28a54e..7afcea4e65 100644 +--- a/tailwind.config.js ++++ b/tailwind.config.js +@@ -10,7 +10,6 @@ config.content = [ + "./libs/tools/send/send-ui/src/*.{html,ts,mdx}", + "./libs/vault/src/**/*.{html,ts,mdx}", + "./apps/web/src/**/*.{html,ts,mdx}", +- "./bitwarden_license/bit-web/src/**/*.{html,ts,mdx}", + "./.storybook/preview.js", + ]; + config.safelist = [ +diff --git a/tsconfig.json b/tsconfig.json +index 225321663c..7a7dde454f 100644 +--- a/tsconfig.json ++++ b/tsconfig.json +@@ -37,8 +37,7 @@ + "@bitwarden/tools-card": ["./libs/tools/card/src"], + "@bitwarden/node/*": ["./libs/node/src/*"], + "@bitwarden/web-vault/*": ["./apps/web/src/*"], +- "@bitwarden/vault": ["./libs/vault/src"], +- "@bitwarden/bit-common/*": ["./bitwarden_license/bit-common/src/*"] ++ "@bitwarden/vault": ["./libs/vault/src"] + }, + "plugins": [ + { +@@ -52,9 +51,7 @@ + "apps/browser/src/**/*", + "libs/*/src/**/*", + "libs/tools/send/**/src/**/*", +- "libs/tools/card/src/**/*", +- "bitwarden_license/bit-web/src/**/*", +- "bitwarden_license/bit-common/src/**/*" ++ "libs/tools/card/src/**/*" + ], + "exclude": [ + "apps/web/src/**/*.spec.ts", diff --git a/resources/src/images/icon-white.svg b/resources/src/images/icon-white.svg new file mode 100644 index 0000000..7697f70 --- /dev/null +++ b/resources/src/images/icon-white.svg @@ -0,0 +1,56 @@ + +Vaultwarden Icon - White + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/src/images/logo-white.svg b/resources/src/images/logo-white.svg new file mode 100644 index 0000000..1ac13d0 --- /dev/null +++ b/resources/src/images/logo-white.svg @@ -0,0 +1,7 @@ + + Vaultwarden + + + + + diff --git a/resources/src/images/logo.svg b/resources/src/images/logo.svg new file mode 100644 index 0000000..1a50969 --- /dev/null +++ b/resources/src/images/logo.svg @@ -0,0 +1,7 @@ + + Vaultwarden + + + + + diff --git a/resources/vaultwarden-icon.svg b/resources/vaultwarden-icon.svg new file mode 100644 index 0000000..90e9b0a --- /dev/null +++ b/resources/vaultwarden-icon.svg @@ -0,0 +1,5 @@ + + Vaultwarden + + + diff --git a/scripts/apply_patches.sh b/scripts/apply_patches.sh index 9eb22c1..ae1f8b4 100755 --- a/scripts/apply_patches.sh +++ b/scripts/apply_patches.sh @@ -48,9 +48,18 @@ replace_embedded_svg_icon \ replace_embedded_svg_icon \ ../resources/vaultwarden-password-manager-logo.svg \ ./apps/web/src/app/layouts/password-manager-logo.ts +replace_embedded_svg_icon \ + ../resources/src/images/logo.svg \ + ./libs/auth/src/angular/icons/bitwarden-logo.icon.ts +replace_embedded_svg_icon \ + ../resources/vaultwarden-icon.svg \ + ./libs/auth/src/angular/icons/bitwarden-shield.icon.ts echo "Remove non-free bitwarden_license/ code" rm -rf ./bitwarden_license/ +if [ -d "./apps/web/src/app/tools/access-intelligence/" ]; then + rm -rf ./apps/web/src/app/tools/access-intelligence/ +fi echo "Using patch: ${PATCH_NAME}" git apply "../patches/${PATCH_NAME}" --reject diff --git a/scripts/generate_patch_file.sh b/scripts/generate_patch_file.sh index af71868..1653a66 100755 --- a/scripts/generate_patch_file.sh +++ b/scripts/generate_patch_file.sh @@ -23,12 +23,17 @@ fi PATCH_FILENAME="${VAULT_VERSION}.patch" if [ "$(git status --porcelain | wc -l)" -ge 1 ]; then - git --no-pager diff --no-color --minimal --abbrev=10 -- . \ + git add -A + git --no-pager diff --cached --no-color --minimal --abbrev=10 -- . \ ':!package-lock.json' \ ':!apps/web/src/favicon.ico' \ + ':!apps/web/src/images/logo.svg' \ + ':!apps/web/src/images/logo-white.svg' \ ':!apps/web/src/images/logo-dark@2x.png' \ ':!apps/web/src/images/logo-white@2x.png' \ ':!apps/web/src/images/icon-white.png' \ + ':!apps/web/src/images/icon-white.svg' \ + ':!apps/web/src/images/icon-dark.png' \ ':!apps/web/src/images/icons/android-chrome-192x192.png' \ ':!apps/web/src/images/icons/android-chrome-512x512.png' \ ':!apps/web/src/images/icons/apple-touch-icon.png' \ @@ -38,8 +43,12 @@ if [ "$(git status --porcelain | wc -l)" -ge 1 ]; then ':!apps/web/src/images/icons/safari-pinned-tab.svg' \ ':!apps/web/src/app/admin-console/icons/admin-console-logo.ts' \ ':!apps/web/src/app/layouts/password-manager-logo.ts' \ + ':!libs/auth/src/angular/icons/bitwarden-logo.icon.ts' \ + ':!libs/auth/src/angular/icons/bitwarden-shield.icon.ts' \ ':!bitwarden_license/' \ + ':!apps/web/src/app/tools/access-intelligence/' \ > "../patches/${PATCH_FILENAME}" + git reset -q echo "Patch has been created here: patches/${PATCH_FILENAME}" else echo "No changes found, skip generating a patch file."