From d4fcb5852a52e29400f74e0bfceac76d979658d7 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 11 Dec 2024 13:20:32 +0100 Subject: [PATCH 1/8] fix: text-drag directive ts-strict error (#12346) --- libs/angular/src/directives/text-drag.directive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/angular/src/directives/text-drag.directive.ts b/libs/angular/src/directives/text-drag.directive.ts index da3e70d1de2..443fbdac157 100644 --- a/libs/angular/src/directives/text-drag.directive.ts +++ b/libs/angular/src/directives/text-drag.directive.ts @@ -17,6 +17,6 @@ export class TextDragDirective { @HostListener("dragstart", ["$event"]) onDragStart(event: DragEvent) { - event.dataTransfer.setData("text", this.data); + event.dataTransfer?.setData("text", this.data); } } From b502e2bc251dfbfee2c5220ab3ccd4629ddcbbd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Wed, 11 Dec 2024 14:47:49 +0000 Subject: [PATCH 2/8] [PM-15154] Domain verification copy update (#12217) * refactor: update domain verification terminology to claimed domains * feat: add description for claimed domains in domain verification * Add informational link to claimed domains description in domain verification component * Update domain verification references to claimed domains in organization layout and SSO component * Add validation message for invalid domain name format in domain verification * Add domain claim messages and descriptions to localization files * Update domain verification navigation text based on feature flag * Update domain verification dialog to support account deprovisioning feature flag * Update domain verification component to support account deprovisioning feature flag * Refactor domain verification dialog to use synchronous feature flag for account deprovisioning * Refactor domain verification routing to resolve title based on account deprovisioning feature flag * Update SSO component to conditionally display domain verification link based on account deprovisioning feature flag * Update event service to conditionally display domain verification messages based on account deprovisioning feature flag * Update domain verification warning message * Refactor domain verification navigation text handling based on account deprovisioning feature flag * Refactor domain verification dialog to use observable for account deprovisioning feature flag * Refactor domain verification component to use observable for account deprovisioning feature flag --- .../organization-layout.component.html | 2 +- .../layouts/organization-layout.component.ts | 9 +++ apps/web/src/app/core/event.service.ts | 17 +++++- apps/web/src/locales/en/messages.json | 61 ++++++++++++++++++- .../domain-add-edit-dialog.component.html | 40 +++++++++--- .../domain-add-edit-dialog.component.ts | 58 ++++++++++++------ .../domain-verification.component.html | 29 ++++++++- .../domain-verification.component.ts | 22 +++++-- .../organizations-routing.module.ts | 13 +++- .../src/app/auth/sso/sso.component.html | 5 +- .../bit-web/src/app/auth/sso/sso.component.ts | 11 +++- 11 files changed, 223 insertions(+), 44 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html index fa4d027d0f6..8387c53e5e3 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html +++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html @@ -98,7 +98,7 @@ *ngIf="canAccessExport$ | async" > 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 91c965658a3..6ead83b01d8 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 @@ -23,6 +23,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga import { ProductTierType } from "@bitwarden/common/billing/enums"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { getById } from "@bitwarden/common/platform/misc"; import { BannerModule, IconModule } from "@bitwarden/components"; @@ -49,6 +50,7 @@ export class OrganizationLayoutComponent implements OnInit { protected readonly logo = AdminConsoleLogo; protected orgFilter = (org: Organization) => canAccessOrgAdmin(org); + protected domainVerificationNavigationTextKey: string; protected integrationPageEnabled$: Observable; @@ -67,6 +69,7 @@ export class OrganizationLayoutComponent implements OnInit { private configService: ConfigService, private policyService: PolicyService, private providerService: ProviderService, + private i18nService: I18nService, ) {} async ngOnInit() { @@ -116,6 +119,12 @@ export class OrganizationLayoutComponent implements OnInit { org.productTierType === ProductTierType.Enterprise && featureFlagEnabled, ), ); + + this.domainVerificationNavigationTextKey = (await this.configService.getFeatureFlag( + FeatureFlag.AccountDeprovisioning, + )) + ? "claimedDomains" + : "domainVerification"; } canShowVaultTab(organization: Organization): boolean { diff --git a/apps/web/src/app/core/event.service.ts b/apps/web/src/app/core/event.service.ts index 412423a3a24..aedad9b26ea 100644 --- a/apps/web/src/app/core/event.service.ts +++ b/apps/web/src/app/core/event.service.ts @@ -6,7 +6,9 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { DeviceType, EventType } from "@bitwarden/common/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { EventResponse } from "@bitwarden/common/models/response/event.response"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @Injectable() @@ -16,6 +18,7 @@ export class EventService { constructor( private i18nService: I18nService, policyService: PolicyService, + private configService: ConfigService, ) { policyService.policies$.subscribe((policies) => { this.policies = policies; @@ -451,10 +454,20 @@ export class EventService { msg = humanReadableMsg = this.i18nService.t("removedDomain", ev.domainName); break; case EventType.OrganizationDomain_Verified: - msg = humanReadableMsg = this.i18nService.t("domainVerifiedEvent", ev.domainName); + msg = humanReadableMsg = this.i18nService.t( + (await this.configService.getFeatureFlag(FeatureFlag.AccountDeprovisioning)) + ? "domainClaimedEvent" + : "domainVerifiedEvent", + ev.domainName, + ); break; case EventType.OrganizationDomain_NotVerified: - msg = humanReadableMsg = this.i18nService.t("domainNotVerifiedEvent", ev.domainName); + msg = humanReadableMsg = this.i18nService.t( + (await this.configService.getFeatureFlag(FeatureFlag.AccountDeprovisioning)) + ? "domainNotClaimedEvent" + : "domainNotVerifiedEvent", + ev.domainName, + ); break; // Secrets Manager case EventType.Secret_Retrieved: diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 06728929912..b1203230688 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -9801,8 +9801,8 @@ "selfHostingTitleProper": { "message": "Self-Hosting" }, - "verified-domain-single-org-warning" : { - "message": "Verifying a domain will turn on the single organization policy." + "claim-domain-single-org-warning" : { + "message": "Claiming a domain will turn on the single organization policy." }, "single-org-revoked-user-warning": { "message": "Non-compliant members will be revoked. Administrators can restore members once they leave all other organizations." @@ -9902,5 +9902,62 @@ }, "removeMembers": { "message": "Remove members" + }, + "claimedDomains": { + "message": "Claimed domains" + }, + "claimDomain": { + "message": "Claim domain" + }, + "reclaimDomain": { + "message": "Reclaim domain" + }, + "claimDomainNameInputHint": { + "message": "Example: mydomain.com. Subdomains require separate entries to be claimed." + }, + "automaticClaimedDomains": { + "message": "Automatic Claimed Domains" + }, + "automaticDomainClaimProcess": { + "message": "Bitwarden will attempt to claim the domain 3 times during the first 72 hours. If the domain can’t be claimed, check the DNS record in your host and manually claim. The domain will be removed from your organization in 7 days if it is not claimed." + }, + "domainNotClaimed": { + "message": "$DOMAIN$ not claimed. Check your DNS records.", + "placeholders": { + "DOMAIN": { + "content": "$1", + "example": "bitwarden.com" + } + } + }, + "domainStatusClaimed": { + "message": "Claimed" + }, + "domainStatusUnderVerification": { + "message": "Under verification" + }, + "claimedDomainsDesc": { + "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts." + }, + "invalidDomainNameClaimMessage": { + "message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed." + }, + "domainClaimedEvent": { + "message": "$DOMAIN$ claimed", + "placeholders": { + "DOMAIN": { + "content": "$1", + "example": "bitwarden.com" + } + } + }, + "domainNotClaimedEvent": { + "message": "$DOMAIN$ not claimed", + "placeholders": { + "DOMAIN": { + "content": "$1", + "example": "bitwarden.com" + } + } } } diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component.html b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component.html index 15120eed92a..7226c957598 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component.html @@ -6,24 +6,37 @@ {{ "newDomain" | i18n }} - {{ "verifyDomain" | i18n }} + + {{ + ((accountDeprovisioningEnabled$ | async) ? "claimDomain" : "verifyDomain") | i18n + }} {{ data.orgDomain.domainName }} {{ - "domainStatusUnverified" | i18n + ((accountDeprovisioningEnabled$ | async) + ? "domainStatusUnderVerification" + : "domainStatusUnverified" + ) | i18n }} {{ - "domainStatusVerified" | i18n + ((accountDeprovisioningEnabled$ | async) ? "domainStatusClaimed" : "domainStatusVerified") + | i18n }}
{{ "domainName" | i18n }} - {{ "domainNameInputHint" | i18n }} + {{ + ((accountDeprovisioningEnabled$ | async) + ? "claimDomainNameInputHint" + : "domainNameInputHint" + ) | i18n + }} @@ -42,18 +55,29 @@ - {{ "automaticDomainVerificationProcess" | i18n }} + {{ + ((accountDeprovisioningEnabled$ | async) + ? "automaticDomainClaimProcess" + : "automaticDomainVerificationProcess" + ) | i18n + }}
+

+ {{ "claimedDomainsDesc" | i18n }} + + + +

+ {{ - "domainStatusUnverified" | i18n + ((accountDeprovisioningEnabled$ | async) + ? "domainStatusUnderVerification" + : "domainStatusUnverified" + ) | i18n }} {{ - "domainStatusVerified" | i18n + ((accountDeprovisioningEnabled$ | async) + ? "domainStatusClaimed" + : "domainStatusVerified" + ) | i18n }} @@ -70,7 +90,10 @@ type="button" > - {{ "verifyDomain" | i18n }} + {{ + ((accountDeprovisioningEnabled$ | async) ? "claimDomain" : "verifyDomain") + | i18n + }} + + + + + {{ "loading" | i18n }} + + + +

{{ "noClientsInList" | i18n }}

+ + + + {{ "name" | i18n }} + {{ "numberOfUsers" | i18n }} + {{ "billingPlan" | i18n }} + + + + + + + + {{ row.organizationName }} + + + {{ row.userCount }} + / {{ row.seats }} + + + {{ row.plan }} + + + + + + + +
diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/clients/vnext-clients.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/vnext-clients.component.ts new file mode 100644 index 00000000000..ba56ce872b2 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/vnext-clients.component.ts @@ -0,0 +1,167 @@ +import { CommonModule } from "@angular/common"; +import { Component } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { FormControl } from "@angular/forms"; +import { ActivatedRoute, Router, RouterModule } from "@angular/router"; +import { firstValueFrom, from, map } from "rxjs"; +import { debounceTime, first, switchMap } from "rxjs/operators"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; +import { ProviderStatusType, ProviderUserType } from "@bitwarden/common/admin-console/enums"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response"; +import { PlanType } from "@bitwarden/common/billing/enums"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; +import { + AvatarModule, + DialogService, + TableDataSource, + TableModule, + ToastService, +} from "@bitwarden/components"; +import { SharedOrganizationModule } from "@bitwarden/web-vault/app/admin-console/organizations/shared"; +import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; + +import { WebProviderService } from "../services/web-provider.service"; + +import { AddOrganizationComponent } from "./add-organization.component"; + +const DisallowedPlanTypes = [ + PlanType.Free, + PlanType.FamiliesAnnually2019, + PlanType.FamiliesAnnually, + PlanType.TeamsStarter2023, + PlanType.TeamsStarter, +]; + +@Component({ + templateUrl: "vnext-clients.component.html", + standalone: true, + imports: [ + SharedOrganizationModule, + HeaderModule, + CommonModule, + JslibModule, + AvatarModule, + RouterModule, + TableModule, + ], +}) +export class vNextClientsComponent { + providerId: string; + addableOrganizations: Organization[]; + loading = true; + manageOrganizations = false; + showAddExisting = false; + dataSource: TableDataSource = + new TableDataSource(); + protected searchControl = new FormControl("", { nonNullable: true }); + + constructor( + private router: Router, + private providerService: ProviderService, + private apiService: ApiService, + private organizationService: OrganizationService, + private organizationApiService: OrganizationApiServiceAbstraction, + private activatedRoute: ActivatedRoute, + private dialogService: DialogService, + private i18nService: I18nService, + private toastService: ToastService, + private validationService: ValidationService, + private webProviderService: WebProviderService, + ) { + this.activatedRoute.queryParams.pipe(first(), takeUntilDestroyed()).subscribe((queryParams) => { + this.searchControl.setValue(queryParams.search); + }); + + this.activatedRoute.parent.params + .pipe( + switchMap((params) => { + this.providerId = params.providerId; + return this.providerService.get$(this.providerId).pipe( + map((provider) => provider?.providerStatus === ProviderStatusType.Billable), + map((isBillable) => { + if (isBillable) { + return from( + this.router.navigate(["../manage-client-organizations"], { + relativeTo: this.activatedRoute, + }), + ); + } else { + return from(this.load()); + } + }), + ); + }), + takeUntilDestroyed(), + ) + .subscribe(); + + this.searchControl.valueChanges + .pipe(debounceTime(200), takeUntilDestroyed()) + .subscribe((searchText) => { + this.dataSource.filter = (data) => + data.organizationName.toLowerCase().indexOf(searchText.toLowerCase()) > -1; + }); + } + + async remove(organization: ProviderOrganizationOrganizationDetailsResponse) { + const confirmed = await this.dialogService.openSimpleDialog({ + title: organization.organizationName, + content: { key: "detachOrganizationConfirmation" }, + type: "warning", + }); + + if (!confirmed) { + return; + } + + try { + await this.webProviderService.detachOrganization(this.providerId, organization.id); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("detachedOrganization", organization.organizationName), + }); + await this.load(); + } catch (e) { + this.validationService.showError(e); + } + } + + async load() { + const response = await this.apiService.getProviderClients(this.providerId); + const clients = response.data != null && response.data.length > 0 ? response.data : []; + this.dataSource.data = clients; + this.manageOrganizations = + (await this.providerService.get(this.providerId)).type === ProviderUserType.ProviderAdmin; + const candidateOrgs = (await this.organizationService.getAll()).filter( + (o) => o.isOwner && o.providerId == null, + ); + const allowedOrgsIds = await Promise.all( + candidateOrgs.map((o) => this.organizationApiService.get(o.id)), + ).then((orgs) => + orgs.filter((o) => !DisallowedPlanTypes.includes(o.planType)).map((o) => o.id), + ); + this.addableOrganizations = candidateOrgs.filter((o) => allowedOrgsIds.includes(o.id)); + + this.showAddExisting = this.addableOrganizations.length !== 0; + this.loading = false; + } + + async addExistingOrganization() { + const dialogRef = AddOrganizationComponent.open(this.dialogService, { + providerId: this.providerId, + organizations: this.addableOrganizations, + }); + + if (await firstValueFrom(dialogRef.closed)) { + await this.load(); + } + } +} diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-routing.module.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-routing.module.ts index 00c944e69bb..09276263332 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-routing.module.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-routing.module.ts @@ -2,8 +2,10 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; import { authGuard } from "@bitwarden/angular/auth/guards"; +import { featureFlaggedRoute } from "@bitwarden/angular/platform/utils/feature-flagged-route"; import { AnonLayoutWrapperComponent } from "@bitwarden/auth/angular"; import { Provider } from "@bitwarden/common/admin-console/models/domain/provider"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { FrontendLayoutComponent } from "@bitwarden/web-vault/app/layouts/frontend-layout.component"; import { UserLayoutComponent } from "@bitwarden/web-vault/app/layouts/user-layout.component"; @@ -12,10 +14,12 @@ import { ProviderSubscriptionComponent, hasConsolidatedBilling, ProviderBillingHistoryComponent, + vNextManageClientsComponent, } from "../../billing/providers"; import { ClientsComponent } from "./clients/clients.component"; import { CreateOrganizationComponent } from "./clients/create-organization.component"; +import { vNextClientsComponent } from "./clients/vnext-clients.component"; import { providerPermissionsGuard } from "./guards/provider-permissions.guard"; import { AcceptProviderComponent } from "./manage/accept-provider.component"; import { EventsComponent } from "./manage/events.component"; @@ -82,13 +86,25 @@ const routes: Routes = [ children: [ { path: "", pathMatch: "full", redirectTo: "clients" }, { path: "clients/create", component: CreateOrganizationComponent }, - { path: "clients", component: ClientsComponent, data: { titleId: "clients" } }, - { - path: "manage-client-organizations", - canActivate: [hasConsolidatedBilling], - component: ManageClientsComponent, - data: { titleId: "clients" }, - }, + ...featureFlaggedRoute({ + defaultComponent: ClientsComponent, + flaggedComponent: vNextClientsComponent, + featureFlag: FeatureFlag.PM12443RemovePagingLogic, + routeOptions: { + path: "clients", + data: { titleId: "clients" }, + }, + }), + ...featureFlaggedRoute({ + defaultComponent: ManageClientsComponent, + flaggedComponent: vNextManageClientsComponent, + featureFlag: FeatureFlag.PM12443RemovePagingLogic, + routeOptions: { + path: "manage-client-organizations", + data: { titleId: "clients" }, + canActivate: [hasConsolidatedBilling], + }, + }), { path: "manage", children: [ diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/index.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/index.ts index ae7bf487f99..f8b344372ef 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/index.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/index.ts @@ -3,3 +3,4 @@ export * from "./manage-clients.component"; export * from "./manage-client-name-dialog.component"; export * from "./manage-client-subscription-dialog.component"; export * from "./no-clients.component"; +export * from "./vnext-manage-clients.component"; diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.html b/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.html new file mode 100644 index 00000000000..c54965bbdb6 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.html @@ -0,0 +1,83 @@ + + + + + {{ "addNewOrganization" | i18n }} + + + + + + {{ "loading" | i18n }} + + + + + + {{ "client" | i18n }} + {{ "assigned" | i18n }} + {{ "used" | i18n }} + {{ "remaining" | i18n }} + {{ "billingPlan" | i18n }} + + + + + + + + + + + {{ row.seats }} + + + {{ row.occupiedSeats }} + + + {{ row.remainingSeats }} + + + {{ row.plan }} + + + + + + + + + + + +
+ +
+
diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.ts new file mode 100644 index 00000000000..5ee7817f34e --- /dev/null +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.ts @@ -0,0 +1,201 @@ +import { Component } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { FormControl } from "@angular/forms"; +import { ActivatedRoute, Router } from "@angular/router"; +import { firstValueFrom, from, lastValueFrom, map } from "rxjs"; +import { debounceTime, first, switchMap } from "rxjs/operators"; + +import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; +import { ProviderStatusType, ProviderUserType } from "@bitwarden/common/admin-console/enums"; +import { Provider } from "@bitwarden/common/admin-console/models/domain/provider"; +import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response"; +import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; +import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; +import { + AvatarModule, + DialogService, + TableDataSource, + TableModule, + ToastService, +} from "@bitwarden/components"; +import { SharedOrganizationModule } from "@bitwarden/web-vault/app/admin-console/organizations/shared"; +import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; + +import { WebProviderService } from "../../../admin-console/providers/services/web-provider.service"; + +import { + CreateClientDialogResultType, + openCreateClientDialog, +} from "./create-client-dialog.component"; +import { + ManageClientNameDialogResultType, + openManageClientNameDialog, +} from "./manage-client-name-dialog.component"; +import { + ManageClientSubscriptionDialogResultType, + openManageClientSubscriptionDialog, +} from "./manage-client-subscription-dialog.component"; +import { vNextNoClientsComponent } from "./vnext-no-clients.component"; + +@Component({ + templateUrl: "vnext-manage-clients.component.html", + standalone: true, + imports: [ + AvatarModule, + TableModule, + HeaderModule, + SharedOrganizationModule, + vNextNoClientsComponent, + ], +}) +export class vNextManageClientsComponent { + providerId: string; + provider: Provider; + loading = true; + isProviderAdmin = false; + dataSource: TableDataSource = + new TableDataSource(); + + protected searchControl = new FormControl("", { nonNullable: true }); + protected plans: PlanResponse[]; + + constructor( + private billingApiService: BillingApiServiceAbstraction, + private providerService: ProviderService, + private router: Router, + private activatedRoute: ActivatedRoute, + private dialogService: DialogService, + private i18nService: I18nService, + private toastService: ToastService, + private validationService: ValidationService, + private webProviderService: WebProviderService, + ) { + this.activatedRoute.queryParams.pipe(first(), takeUntilDestroyed()).subscribe((queryParams) => { + this.searchControl.setValue(queryParams.search); + }); + + this.activatedRoute.parent.params + .pipe( + switchMap((params) => { + this.providerId = params.providerId; + return this.providerService.get$(this.providerId).pipe( + map((provider: Provider) => provider?.providerStatus === ProviderStatusType.Billable), + map((isBillable) => { + if (!isBillable) { + return from( + this.router.navigate(["../clients"], { + relativeTo: this.activatedRoute, + }), + ); + } else { + return from(this.load()); + } + }), + ); + }), + takeUntilDestroyed(), + ) + .subscribe(); + + this.searchControl.valueChanges + .pipe(debounceTime(200), takeUntilDestroyed()) + .subscribe((searchText) => { + this.dataSource.filter = (data) => + data.organizationName.toLowerCase().indexOf(searchText.toLowerCase()) > -1; + }); + } + + async load() { + this.provider = await firstValueFrom(this.providerService.get$(this.providerId)); + + this.isProviderAdmin = this.provider.type === ProviderUserType.ProviderAdmin; + + const clients = (await this.billingApiService.getProviderClientOrganizations(this.providerId)) + .data; + + clients.forEach((client) => (client.plan = client.plan.replace(" (Monthly)", ""))); + + this.dataSource.data = clients; + + this.plans = (await this.billingApiService.getPlans()).data; + + this.loading = false; + } + + createClient = async () => { + const reference = openCreateClientDialog(this.dialogService, { + data: { + providerId: this.providerId, + plans: this.plans, + }, + }); + + const result = await lastValueFrom(reference.closed); + + if (result === CreateClientDialogResultType.Submitted) { + await this.load(); + } + }; + + manageClientName = async (organization: ProviderOrganizationOrganizationDetailsResponse) => { + const dialogRef = openManageClientNameDialog(this.dialogService, { + data: { + providerId: this.providerId, + organization: { + id: organization.id, + name: organization.organizationName, + seats: organization.seats, + }, + }, + }); + + const result = await firstValueFrom(dialogRef.closed); + + if (result === ManageClientNameDialogResultType.Submitted) { + await this.load(); + } + }; + + manageClientSubscription = async ( + organization: ProviderOrganizationOrganizationDetailsResponse, + ) => { + const dialogRef = openManageClientSubscriptionDialog(this.dialogService, { + data: { + organization, + provider: this.provider, + }, + }); + + const result = await firstValueFrom(dialogRef.closed); + + if (result === ManageClientSubscriptionDialogResultType.Submitted) { + await this.load(); + } + }; + + async remove(organization: ProviderOrganizationOrganizationDetailsResponse) { + const confirmed = await this.dialogService.openSimpleDialog({ + title: organization.organizationName, + content: { key: "detachOrganizationConfirmation" }, + type: "warning", + }); + + if (!confirmed) { + return; + } + + try { + await this.webProviderService.detachOrganization(this.providerId, organization.id); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("detachedOrganization", organization.organizationName), + }); + await this.load(); + } catch (e) { + this.validationService.showError(e); + } + } +} diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-no-clients.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-no-clients.component.ts new file mode 100644 index 00000000000..5ad19945c51 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-no-clients.component.ts @@ -0,0 +1,50 @@ +import { Component, EventEmitter, Input, Output } from "@angular/core"; + +import { svgIcon } from "@bitwarden/components"; +import { SharedOrganizationModule } from "@bitwarden/web-vault/app/admin-console/organizations/shared"; + +const gearIcon = svgIcon` + + + + + + + + + + + + + + + + +`; + +@Component({ + selector: "app-no-clients", + standalone: true, + imports: [SharedOrganizationModule], + template: `
+ +

{{ "noClients" | i18n }}

+ + + {{ "addNewOrganization" | i18n }} + +
`, +}) +export class vNextNoClientsComponent { + icon = gearIcon; + @Input() showAddOrganizationButton = true; + @Output() addNewOrganizationClicked = new EventEmitter(); + + addNewOrganization = () => this.addNewOrganizationClicked.emit(); +} diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index f79ebf8aa55..6597c97b641 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -40,6 +40,7 @@ export enum FeatureFlag { DisableFreeFamiliesSponsorship = "PM-12274-disable-free-families-sponsorship", MacOsNativeCredentialSync = "macos-native-credential-sync", PM11360RemoveProviderExportPermission = "pm-11360-remove-provider-export-permission", + PM12443RemovePagingLogic = "pm-12443-remove-paging-logic", } export type AllowedFeatureFlagTypes = boolean | number | string; @@ -90,6 +91,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.DisableFreeFamiliesSponsorship]: FALSE, [FeatureFlag.MacOsNativeCredentialSync]: FALSE, [FeatureFlag.PM11360RemoveProviderExportPermission]: FALSE, + [FeatureFlag.PM12443RemovePagingLogic]: FALSE, } satisfies Record; export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue; From 92a620dd9c06c46127164d3c7a103aeafff92708 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 11 Dec 2024 07:10:06 -0800 Subject: [PATCH 4/8] [BEEEP/PM-10534] Add snap biometric support (#12187) * Add snap biometric support * Fix linting * Remove unused message * Disable snap browser integration again --- apps/desktop/electron-builder.json | 11 ++++++++++- apps/desktop/package.json | 2 +- .../resources/com.bitwarden.desktop.policy | 16 ++++++++++++++++ .../biometrics/biometric.unix.main.ts | 10 +++++++--- apps/desktop/src/locales/en/messages.json | 3 --- 5 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 apps/desktop/resources/com.bitwarden.desktop.policy diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index 9b894b0bfc7..38f11a97a8b 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -241,7 +241,16 @@ "autoStart": true, "base": "core22", "confinement": "strict", - "plugs": ["default", "network-bind", "password-manager-service"], + "plugs": [ + "default", + "network-bind", + "password-manager-service", + { + "polkit": { + "action-prefix": "com.bitwarden.Bitwarden" + } + } + ], "stagePackages": ["default"] }, "protocols": [ diff --git a/apps/desktop/package.json b/apps/desktop/package.json index eab9a7d7119..f546563ed18 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -35,7 +35,7 @@ "clean:dist": "rimraf ./dist", "pack:dir": "npm run clean:dist && electron-builder --dir -p never", "pack:lin:flatpak": "npm run clean:dist && electron-builder --dir -p never && flatpak-builder --repo=build/.repo build/.flatpak ./resources/com.bitwarden.desktop.devel.yaml --install-deps-from=flathub --force-clean && flatpak build-bundle ./build/.repo/ ./dist/com.bitwarden.desktop.flatpak com.bitwarden.desktop", - "pack:lin": "npm run clean:dist && electron-builder --linux --x64 -p never", + "pack:lin": "npm run clean:dist && electron-builder --linux --x64 -p never && export SNAP_FILE=$(realpath ./dist/bitwarden_*.snap) && unsquashfs -d ./dist/tmp-snap/ $SNAP_FILE && mkdir -p ./dist/tmp-snap/meta/polkit/ && cp ./resources/com.bitwarden.desktop.policy ./dist/tmp-snap/meta/polkit/polkit.com.bitwarden.desktop.policy && rm $SNAP_FILE && mksquashfs ./dist/tmp-snap/ $SNAP_FILE -noappend -comp lzo -no-fragments && rm -rf ./dist/tmp-snap/", "pack:mac": "npm run clean:dist && electron-builder --mac --universal -p never", "pack:mac:arm64": "npm run clean:dist && electron-builder --mac --arm64 -p never", "pack:mac:mas": "npm run clean:dist && electron-builder --mac mas --universal -p never", diff --git a/apps/desktop/resources/com.bitwarden.desktop.policy b/apps/desktop/resources/com.bitwarden.desktop.policy new file mode 100644 index 00000000000..e48bc6b8fbb --- /dev/null +++ b/apps/desktop/resources/com.bitwarden.desktop.policy @@ -0,0 +1,16 @@ + + + + + + Unlock Bitwarden + Authenticate to unlock Bitwarden + + no + no + auth_self + + + diff --git a/apps/desktop/src/key-management/biometrics/biometric.unix.main.ts b/apps/desktop/src/key-management/biometrics/biometric.unix.main.ts index 771f1ea3a1c..f2bcf62e03e 100644 --- a/apps/desktop/src/key-management/biometrics/biometric.unix.main.ts +++ b/apps/desktop/src/key-management/biometrics/biometric.unix.main.ts @@ -87,8 +87,8 @@ export default class BiometricUnixMain implements OsBiometricService { } async authenticateBiometric(): Promise { - const hwnd = this.windowMain.win.getNativeWindowHandle(); - return await biometrics.prompt(hwnd, this.i18nservice.t("polkitConsentMessage")); + const hwnd = Buffer.from(""); + return await biometrics.prompt(hwnd, ""); } async osSupportsBiometric(): Promise { @@ -98,10 +98,14 @@ export default class BiometricUnixMain implements OsBiometricService { // This could be dynamically detected on dbus in the future. // We should check if a libsecret implementation is available on the system // because otherwise we cannot offlod the protected userkey to secure storage. - return (await passwords.isAvailable()) && !isSnapStore(); + return await passwords.isAvailable(); } async osBiometricsNeedsSetup(): Promise { + if (isSnapStore()) { + return false; + } + // check whether the polkit policy is loaded via dbus call to polkit return !(await biometrics.available()); } diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index e4c235dada9..f8f81a5ac2c 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -1734,9 +1734,6 @@ "windowsHelloConsentMessage": { "message": "Verify for Bitwarden." }, - "polkitConsentMessage": { - "message": "Authenticate to unlock Bitwarden." - }, "unlockWithTouchId": { "message": "Unlock with Touch ID" }, From 8dd904f4b7ad767a24839a18b878703514be1f87 Mon Sep 17 00:00:00 2001 From: Brandon Treston Date: Wed, 11 Dec 2024 19:08:14 -0500 Subject: [PATCH 6/8] fix ts strict errors (#12355) --- .../clients/vnext-clients.component.ts | 10 +++++----- .../clients/vnext-manage-clients.component.ts | 20 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/clients/vnext-clients.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/vnext-clients.component.ts index ba56ce872b2..2be38477d4c 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/clients/vnext-clients.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/vnext-clients.component.ts @@ -53,8 +53,8 @@ const DisallowedPlanTypes = [ ], }) export class vNextClientsComponent { - providerId: string; - addableOrganizations: Organization[]; + providerId: string = ""; + addableOrganizations: Organization[] = []; loading = true; manageOrganizations = false; showAddExisting = false; @@ -79,8 +79,8 @@ export class vNextClientsComponent { this.searchControl.setValue(queryParams.search); }); - this.activatedRoute.parent.params - .pipe( + this.activatedRoute.parent?.params + ?.pipe( switchMap((params) => { this.providerId = params.providerId; return this.providerService.get$(this.providerId).pipe( @@ -125,7 +125,7 @@ export class vNextClientsComponent { await this.webProviderService.detachOrganization(this.providerId, organization.id); this.toastService.showToast({ variant: "success", - title: null, + title: "", message: this.i18nService.t("detachedOrganization", organization.organizationName), }); await this.load(); diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.ts index 5ee7817f34e..4c0837d6da2 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.ts @@ -51,15 +51,15 @@ import { vNextNoClientsComponent } from "./vnext-no-clients.component"; ], }) export class vNextManageClientsComponent { - providerId: string; - provider: Provider; + providerId: string = ""; + provider: Provider | undefined; loading = true; isProviderAdmin = false; dataSource: TableDataSource = new TableDataSource(); protected searchControl = new FormControl("", { nonNullable: true }); - protected plans: PlanResponse[]; + protected plans: PlanResponse[] = []; constructor( private billingApiService: BillingApiServiceAbstraction, @@ -76,8 +76,8 @@ export class vNextManageClientsComponent { this.searchControl.setValue(queryParams.search); }); - this.activatedRoute.parent.params - .pipe( + this.activatedRoute.parent?.params + ?.pipe( switchMap((params) => { this.providerId = params.providerId; return this.providerService.get$(this.providerId).pipe( @@ -110,12 +110,12 @@ export class vNextManageClientsComponent { async load() { this.provider = await firstValueFrom(this.providerService.get$(this.providerId)); - this.isProviderAdmin = this.provider.type === ProviderUserType.ProviderAdmin; + this.isProviderAdmin = this.provider?.type === ProviderUserType.ProviderAdmin; const clients = (await this.billingApiService.getProviderClientOrganizations(this.providerId)) .data; - clients.forEach((client) => (client.plan = client.plan.replace(" (Monthly)", ""))); + clients.forEach((client) => (client.plan = client.plan?.replace(" (Monthly)", ""))); this.dataSource.data = clients; @@ -146,7 +146,7 @@ export class vNextManageClientsComponent { organization: { id: organization.id, name: organization.organizationName, - seats: organization.seats, + seats: organization.seats ? organization.seats : 0, }, }, }); @@ -164,7 +164,7 @@ export class vNextManageClientsComponent { const dialogRef = openManageClientSubscriptionDialog(this.dialogService, { data: { organization, - provider: this.provider, + provider: this.provider!, }, }); @@ -190,7 +190,7 @@ export class vNextManageClientsComponent { await this.webProviderService.detachOrganization(this.providerId, organization.id); this.toastService.showToast({ variant: "success", - title: null, + title: "", message: this.i18nService.t("detachedOrganization", organization.organizationName), }); await this.load(); From cecf1f2506a69b8a6edc16be19e772531785a5b7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 10:26:05 +0100 Subject: [PATCH 7/8] [deps] Platform: Update electron to v33 - abandoned (#11580) * [deps] Platform: Update electron to v33 * fix: remove event from minimize * chore: update electron version in `electron-builder.json` --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Andreas Coroiu Co-authored-by: Bernd Schoolmann --- apps/desktop/electron-builder.json | 2 +- apps/desktop/src/main/tray.main.ts | 3 +-- package-lock.json | 14 +++++++------- package.json | 2 +- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index 38f11a97a8b..898ad086b29 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -20,7 +20,7 @@ "**/node_modules/@bitwarden/desktop-napi/index.js", "**/node_modules/@bitwarden/desktop-napi/desktop_napi.${platform}-${arch}*.node" ], - "electronVersion": "32.1.1", + "electronVersion": "33.2.1", "generateUpdatesFilesForAllChannels": true, "publish": { "provider": "generic", diff --git a/apps/desktop/src/main/tray.main.ts b/apps/desktop/src/main/tray.main.ts index 641af8db0ad..52a8615a1da 100644 --- a/apps/desktop/src/main/tray.main.ts +++ b/apps/desktop/src/main/tray.main.ts @@ -64,9 +64,8 @@ export class TrayMain { } setupWindowListeners(win: BrowserWindow) { - win.on("minimize", async (e: Event) => { + win.on("minimize", async () => { if (await firstValueFrom(this.desktopSettingsService.minimizeToTray$)) { - e.preventDefault(); // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises this.hideToTray(); diff --git a/package-lock.json b/package-lock.json index 64a7c926ca2..2da7d9e6255 100644 --- a/package-lock.json +++ b/package-lock.json @@ -132,7 +132,7 @@ "copy-webpack-plugin": "12.0.2", "cross-env": "7.0.3", "css-loader": "7.1.2", - "electron": "32.1.1", + "electron": "33.2.1", "electron-builder": "24.13.3", "electron-log": "5.2.4", "electron-reload": "2.0.0-alpha.1", @@ -15745,9 +15745,9 @@ } }, "node_modules/electron": { - "version": "32.1.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-32.1.1.tgz", - "integrity": "sha512-NlWvG6kXOJbZbELmzP3oV7u50I3NHYbCeh+AkUQ9vGyP7b74cFMx9HdTzejODeztW1jhr3SjIBbUZzZ45zflfQ==", + "version": "33.2.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-33.2.1.tgz", + "integrity": "sha512-SG/nmSsK9Qg1p6wAW+ZfqU+AV8cmXMTIklUL18NnOKfZLlum4ZsDoVdmmmlL39ZmeCaq27dr7CgslRPahfoVJg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -15986,9 +15986,9 @@ } }, "node_modules/electron/node_modules/@types/node": { - "version": "20.17.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz", - "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==", + "version": "20.17.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.8.tgz", + "integrity": "sha512-ahz2g6/oqbKalW9sPv6L2iRbhLnojxjYWspAqhjvqSWBgGebEJT5GvRmk0QXPj3sbC6rU0GTQjPLQkmR8CObvA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 5573332db1a..aa567f18df6 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "copy-webpack-plugin": "12.0.2", "cross-env": "7.0.3", "css-loader": "7.1.2", - "electron": "32.1.1", + "electron": "33.2.1", "electron-builder": "24.13.3", "electron-log": "5.2.4", "electron-reload": "2.0.0-alpha.1", From f8c33ea04be4052a383c6f1ce84bca6ff3a07256 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Thu, 12 Dec 2024 11:50:21 +0100 Subject: [PATCH 8/8] [PM-15126] Tighten scope of our client build pipelines to remove reliance on secrets (#12243) * feat: create copy of desktop build for PR target * chore: add temporary file to trigger ci * fix: remove check-run from regular desktop build * feat: change browser build to not use pr target * fix: skip build-safari if secret is not available * feat: skip safari build if secrets are not available * feat: let windows desktop build without secrets * fix: has_secrets not being output correctly * feat: let macos desktop build without secrets * feat: don't build browser as part of desktop * feat: change CLI to pull_request * feat: let web build without secrets * feat: tweak lint to run on PR and not just push * feat: add PR target workflows * fix: remove wip files * fix: lint on hotfix-rc branches * feat: add new workflows to CODEOWNERS --- .github/CODEOWNERS | 4 ++ .github/workflows/build-browser-target.yml | 39 +++++++++++++ .github/workflows/build-browser.yml | 18 +++--- .github/workflows/build-cli-target.yml | 39 +++++++++++++ .github/workflows/build-cli.yml | 27 +++++---- .github/workflows/build-desktop-target.yml | 38 ++++++++++++ .github/workflows/build-desktop.yml | 68 +++++++++++++++++----- .github/workflows/build-web-target.yml | 41 +++++++++++++ .github/workflows/build-web.yml | 28 ++++++--- .github/workflows/lint.yml | 10 +++- 10 files changed, 269 insertions(+), 43 deletions(-) create mode 100644 .github/workflows/build-browser-target.yml create mode 100644 .github/workflows/build-cli-target.yml create mode 100644 .github/workflows/build-desktop-target.yml create mode 100644 .github/workflows/build-web-target.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 99bea676bfb..e9360c73ab9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -85,9 +85,13 @@ apps/web/src/app/shared @bitwarden/team-platform-dev apps/web/src/translation-constants.ts @bitwarden/team-platform-dev # Workflows .github/workflows/brew-bump-desktop.yml @bitwarden/team-platform-dev +.github/workflows/build-browser-target.yml @bitwarden/team-platform-dev .github/workflows/build-browser.yml @bitwarden/team-platform-dev +.github/workflows/build-cli-target.yml @bitwarden/team-platform-dev .github/workflows/build-cli.yml @bitwarden/team-platform-dev +.github/workflows/build-desktop-target.yml @bitwarden/team-platform-dev .github/workflows/build-desktop.yml @bitwarden/team-platform-dev +.github/workflows/build-web-target.yml @bitwarden/team-platform-dev .github/workflows/build-web.yml @bitwarden/team-platform-dev .github/workflows/chromatic.yml @bitwarden/team-platform-dev .github/workflows/lint.yml @bitwarden/team-platform-dev diff --git a/.github/workflows/build-browser-target.yml b/.github/workflows/build-browser-target.yml new file mode 100644 index 00000000000..11a268466f1 --- /dev/null +++ b/.github/workflows/build-browser-target.yml @@ -0,0 +1,39 @@ +name: Build Browser on PR Target + +on: + pull_request: + types: [opened, synchronize] + branches-ignore: + - 'l10n_master' + - 'cf-pages' + paths: + - 'apps/browser/**' + - 'libs/**' + - '*' + - '!*.md' + - '!*.txt' + workflow_call: + inputs: {} + workflow_dispatch: + inputs: + sdk_branch: + description: "Custom SDK branch" + required: false + type: string + +defaults: + run: + shell: bash + +jobs: + check-run: + name: Check PR run + uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main + + run-workflow: + name: Run Build Browser on PR Target + needs: check-run + if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} + uses: ./.github/workflows/build-browser.yml + secrets: inherit + diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 7740e418e7b..56a980bf0f9 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -1,7 +1,7 @@ name: Build Browser on: - pull_request_target: + pull_request: types: [opened, synchronize] branches-ignore: - 'l10n_master' @@ -38,19 +38,14 @@ defaults: shell: bash jobs: - check-run: - name: Check PR run - uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main - setup: name: Setup runs-on: ubuntu-22.04 - needs: - - check-run outputs: repo_url: ${{ steps.gen_vars.outputs.repo_url }} adj_build_number: ${{ steps.gen_vars.outputs.adj_build_number }} node_version: ${{ steps.retrieve-node-version.outputs.node_version }} + has_secrets: ${{ steps.check-secrets.outputs.has_secrets }} steps: - name: Check out repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -74,6 +69,14 @@ jobs: NODE_VERSION=${NODE_NVMRC/v/''} echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT + - name: Check secrets + id: check-secrets + env: + AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + run: | + has_secrets=${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL != '' }} + echo "has_secrets=$has_secrets" >> $GITHUB_OUTPUT + locales-test: name: Locales Test @@ -281,6 +284,7 @@ jobs: needs: - setup - locales-test + if: ${{ needs.setup.outputs.has_secrets == 'true' }} env: _BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} diff --git a/.github/workflows/build-cli-target.yml b/.github/workflows/build-cli-target.yml new file mode 100644 index 00000000000..658d8f922ba --- /dev/null +++ b/.github/workflows/build-cli-target.yml @@ -0,0 +1,39 @@ +name: Build CLI on PR Target + +on: + pull_request: + types: [opened, synchronize] + branches-ignore: + - 'l10n_master' + - 'cf-pages' + paths: + - 'apps/cli/**' + - 'libs/**' + - '*' + - '!*.md' + - '!*.txt' + - '.github/workflows/build-cli.yml' + - 'bitwarden_license/bit-cli/**' + workflow_dispatch: + inputs: + sdk_branch: + description: "Custom SDK branch" + required: false + type: string + +defaults: + run: + shell: bash + +jobs: + check-run: + name: Check PR run + uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main + + run-workflow: + name: Run Build CLI on PR Target + needs: check-run + if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} + uses: ./.github/workflows/build-cli.yml + secrets: inherit + diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index d480879fb15..35970a8b307 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -1,7 +1,7 @@ name: Build CLI on: - pull_request_target: + pull_request: types: [opened, synchronize] branches-ignore: - 'l10n_master' @@ -27,6 +27,8 @@ on: - '!*.txt' - '.github/workflows/build-cli.yml' - 'bitwarden_license/bit-cli/**' + workflow_call: + inputs: {} workflow_dispatch: inputs: sdk_branch: @@ -39,18 +41,13 @@ defaults: working-directory: apps/cli jobs: - check-run: - name: Check PR run - uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main - setup: name: Setup runs-on: ubuntu-22.04 - needs: - - check-run outputs: package_version: ${{ steps.retrieve-package-version.outputs.package_version }} node_version: ${{ steps.retrieve-node-version.outputs.node_version }} + has_secrets: ${{ steps.check-secrets.outputs.has_secrets }} steps: - name: Check out repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -71,6 +68,14 @@ jobs: NODE_VERSION=${NODE_NVMRC/v/''} echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT + - name: Check secrets + id: check-secrets + env: + AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + run: | + has_secrets=${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL != '' }} + echo "has_secrets=$has_secrets" >> $GITHUB_OUTPUT + cli: name: CLI ${{ matrix.os.base }} - ${{ matrix.license_type.readable }} strategy: @@ -117,7 +122,7 @@ jobs: working-directory: ./ - name: Download SDK Artifacts - if: ${{ inputs.sdk_branch != '' }} + if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }} uses: bitwarden/gh-actions/download-artifacts@main with: github_token: ${{secrets.GITHUB_TOKEN}} @@ -130,7 +135,7 @@ jobs: if_no_artifact_found: fail - name: Override SDK - if: ${{ inputs.sdk_branch != '' }} + if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }} working-directory: ./ run: | ls -l ../ @@ -272,7 +277,7 @@ jobs: working-directory: ./ - name: Download SDK Artifacts - if: ${{ inputs.sdk_branch != '' }} + if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }} uses: bitwarden/gh-actions/download-artifacts@main with: github_token: ${{secrets.GITHUB_TOKEN}} @@ -285,7 +290,7 @@ jobs: if_no_artifact_found: fail - name: Override SDK - if: ${{ inputs.sdk_branch != '' }} + if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }} working-directory: ./ run: | ls -l ../ diff --git a/.github/workflows/build-desktop-target.yml b/.github/workflows/build-desktop-target.yml new file mode 100644 index 00000000000..47f85d69163 --- /dev/null +++ b/.github/workflows/build-desktop-target.yml @@ -0,0 +1,38 @@ +name: Build Desktop on PR Target + +on: + pull_request: + types: [opened, synchronize] + branches-ignore: + - 'l10n_master' + - 'cf-pages' + paths: + - 'apps/desktop/**' + - 'libs/**' + - '*' + - '!*.md' + - '!*.txt' + - '.github/workflows/build-desktop.yml' + workflow_dispatch: + inputs: + sdk_branch: + description: "Custom SDK branch" + required: false + type: string + +defaults: + run: + shell: bash + +jobs: + check-run: + name: Check PR run + uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main + + run-workflow: + name: Run Build Desktop on PR Target + needs: check-run + if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} + uses: ./.github/workflows/build-desktop.yml + secrets: inherit + diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index bc9bdec396a..e35dee54e08 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -1,7 +1,7 @@ name: Build Desktop on: - pull_request_target: + pull_request: types: [opened, synchronize] branches-ignore: - 'l10n_master' @@ -25,6 +25,8 @@ on: - '!*.md' - '!*.txt' - '.github/workflows/build-desktop.yml' + workflow_call: + inputs: {} workflow_dispatch: inputs: sdk_branch: @@ -37,15 +39,9 @@ defaults: shell: bash jobs: - check-run: - name: Check PR run - uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main - electron-verify: name: Verify Electron Version runs-on: ubuntu-22.04 - needs: - - check-run steps: - name: Check out repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -67,8 +63,6 @@ jobs: setup: name: Setup runs-on: ubuntu-22.04 - needs: - - check-run outputs: package_version: ${{ steps.retrieve-version.outputs.package_version }} release_channel: ${{ steps.release-channel.outputs.channel }} @@ -76,6 +70,7 @@ jobs: rc_branch_exists: ${{ steps.branch-check.outputs.rc_branch_exists }} hotfix_branch_exists: ${{ steps.branch-check.outputs.hotfix_branch_exists }} node_version: ${{ steps.retrieve-node-version.outputs.node_version }} + has_secrets: ${{ steps.check-secrets.outputs.has_secrets }} defaults: run: working-directory: apps/desktop @@ -138,6 +133,14 @@ jobs: NODE_VERSION=${NODE_NVMRC/v/''} echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT + - name: Check secrets + id: check-secrets + env: + AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + run: | + has_secrets=${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL != '' }} + echo "has_secrets=$has_secrets" >> $GITHUB_OUTPUT + linux: name: Linux Build # Note, before updating the ubuntu version of the workflow, ensure the snap base image @@ -333,12 +336,14 @@ jobs: rustup show - name: Login to Azure + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: bitwarden/gh-actions/get-keyvault-secrets@main with: keyvault: "bitwarden-ci" @@ -353,7 +358,7 @@ jobs: working-directory: ./ - name: Download SDK Artifacts - if: ${{ inputs.sdk_branch != '' }} + if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }} uses: bitwarden/gh-actions/download-artifacts@main with: github_token: ${{secrets.GITHUB_TOKEN}} @@ -366,7 +371,7 @@ jobs: if_no_artifact_found: fail - name: Override SDK - if: ${{ inputs.sdk_branch != '' }} + if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }} working-directory: ./ run: | ls -l ../ @@ -386,7 +391,17 @@ jobs: working-directory: apps/desktop/desktop_native run: node build.js cross-platform - - name: Build & Sign (dev) + - name: Build + run: | + npm run build + + - name: Pack + if: ${{ needs.setup.outputs.has_secrets == 'false' }} + run: | + npm run pack:win + + - name: Pack & Sign (dev) + if: ${{ needs.setup.outputs.has_secrets == 'true' }} env: ELECTRON_BUILDER_SIGN: 1 SIGNING_VAULT_URL: ${{ steps.retrieve-secrets.outputs.code-signing-vault-url }} @@ -395,10 +410,10 @@ jobs: SIGNING_CLIENT_SECRET: ${{ steps.retrieve-secrets.outputs.code-signing-client-secret }} SIGNING_CERT_NAME: ${{ steps.retrieve-secrets.outputs.code-signing-cert-name }} run: | - npm run build npm run pack:win - name: Rename appx files for store + if: ${{ needs.setup.outputs.has_secrets == 'true' }} run: | Copy-Item "./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx" ` -Destination "./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx" @@ -408,6 +423,7 @@ jobs: -Destination "./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx" - name: Package for Chocolatey + if: ${{ needs.setup.outputs.has_secrets == 'true' }} run: | Copy-Item -Path ./stores/chocolatey -Destination ./dist/chocolatey -Recurse Copy-Item -Path ./dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe ` @@ -419,6 +435,7 @@ jobs: choco pack ./dist/chocolatey/bitwarden.nuspec --version "$env:_PACKAGE_VERSION" --out ./dist/chocolatey - name: Fix NSIS artifact names for auto-updater + if: ${{ needs.setup.outputs.has_secrets == 'true' }} run: | Rename-Item -Path .\dist\nsis-web\Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z ` -NewName bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z @@ -435,6 +452,7 @@ jobs: if-no-files-found: error - name: Upload installer exe artifact + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe @@ -442,6 +460,7 @@ jobs: if-no-files-found: error - name: Upload appx ia32 artifact + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx @@ -449,6 +468,7 @@ jobs: if-no-files-found: error - name: Upload store appx ia32 artifact + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx @@ -456,6 +476,7 @@ jobs: if-no-files-found: error - name: Upload NSIS ia32 artifact + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z @@ -463,6 +484,7 @@ jobs: if-no-files-found: error - name: Upload appx x64 artifact + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx @@ -470,6 +492,7 @@ jobs: if-no-files-found: error - name: Upload store appx x64 artifact + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx @@ -477,6 +500,7 @@ jobs: if-no-files-found: error - name: Upload NSIS x64 artifact + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z @@ -484,6 +508,7 @@ jobs: if-no-files-found: error - name: Upload appx ARM64 artifact + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx @@ -491,6 +516,7 @@ jobs: if-no-files-found: error - name: Upload store appx ARM64 artifact + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx @@ -498,6 +524,7 @@ jobs: if-no-files-found: error - name: Upload NSIS ARM64 artifact + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z @@ -505,6 +532,7 @@ jobs: if-no-files-found: error - name: Upload nupkg artifact + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: bitwarden.${{ env._PACKAGE_VERSION }}.nupkg @@ -512,6 +540,7 @@ jobs: if-no-files-found: error - name: Upload auto-update artifact + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: ${{ needs.setup.outputs.release_channel }}.yml @@ -574,11 +603,13 @@ jobs: key: ${{ runner.os }}-${{ github.run_id }}-safari-extension - name: Login to Azure + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Download Provisioning Profiles secrets + if: ${{ needs.setup.outputs.has_secrets == 'true' }} env: ACCOUNT_NAME: bitwardenci CONTAINER_NAME: profiles @@ -591,6 +622,7 @@ jobs: --output none - name: Get certificates + if: ${{ needs.setup.outputs.has_secrets == 'true' }} run: | mkdir -p $HOME/certificates @@ -613,6 +645,7 @@ jobs: jq -r .value | base64 -d > $HOME/certificates/macdev-cert.p12 - name: Set up keychain + if: ${{ needs.setup.outputs.has_secrets == 'true' }} env: KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} run: | @@ -642,6 +675,7 @@ jobs: security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain - name: Set up provisioning profiles + if: ${{ needs.setup.outputs.has_secrets == 'true' }} run: | cp $HOME/secrets/bitwarden_desktop_appstore.provisionprofile \ $GITHUB_WORKSPACE/apps/desktop/bitwarden_desktop_appstore.provisionprofile @@ -661,7 +695,7 @@ jobs: working-directory: ./ - name: Download SDK Artifacts - if: ${{ inputs.sdk_branch != '' }} + if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }} uses: bitwarden/gh-actions/download-artifacts@main with: github_token: ${{secrets.GITHUB_TOKEN}} @@ -674,7 +708,7 @@ jobs: if_no_artifact_found: fail - name: Override SDK - if: ${{ inputs.sdk_branch != '' }} + if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }} working-directory: ./ run: | ls -l ../ @@ -701,6 +735,7 @@ jobs: browser-build: name: Browser Build needs: setup + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: ./.github/workflows/build-browser.yml secrets: inherit @@ -708,6 +743,7 @@ jobs: macos-package-github: name: MacOS Package GitHub Release Assets runs-on: macos-13 + if: ${{ needs.setup.outputs.has_secrets == 'true' }} needs: - browser-build - macos-build @@ -949,6 +985,7 @@ jobs: macos-package-mas: name: MacOS Package Prod Release Asset runs-on: macos-13 + if: ${{ needs.setup.outputs.has_secrets == 'true' }} needs: - browser-build - macos-build @@ -1216,6 +1253,7 @@ jobs: macos-package-dev: name: MacOS Package Dev Release Asset runs-on: macos-13 + if: ${{ needs.setup.outputs.has_secrets == 'true' }} needs: - browser-build - macos-build diff --git a/.github/workflows/build-web-target.yml b/.github/workflows/build-web-target.yml new file mode 100644 index 00000000000..a27af0b0870 --- /dev/null +++ b/.github/workflows/build-web-target.yml @@ -0,0 +1,41 @@ +name: Build Web on PR Target + +on: + pull_request: + types: [opened, synchronize] + branches-ignore: + - 'l10n_master' + - 'cf-pages' + paths: + - 'apps/web/**' + - 'libs/**' + - '*' + - '!*.md' + - '!*.txt' + - '.github/workflows/build-web.yml' + workflow_dispatch: + inputs: + custom_tag_extension: + description: "Custom image tag extension" + required: false + sdk_branch: + description: "Custom SDK branch" + required: false + type: string + +defaults: + run: + shell: bash + +jobs: + check-run: + name: Check PR run + uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main + + run-workflow: + name: Run Build Web on PR Target + needs: check-run + if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} + uses: ./.github/workflows/build-web.yml + secrets: inherit + diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 6e5e11c3361..2360f876826 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -1,7 +1,7 @@ name: Build Web on: - pull_request_target: + pull_request: types: [opened, synchronize] branches-ignore: - 'l10n_master' @@ -27,6 +27,8 @@ on: - '.github/workflows/build-web.yml' release: types: [published] + workflow_call: + inputs: {} workflow_dispatch: inputs: custom_tag_extension: @@ -41,18 +43,13 @@ env: _AZ_REGISTRY: bitwardenprod.azurecr.io jobs: - check-run: - name: Check PR run - uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main - setup: name: Setup runs-on: ubuntu-22.04 - needs: - - check-run outputs: version: ${{ steps.version.outputs.value }} node_version: ${{ steps.retrieve-node-version.outputs.node_version }} + has_secrets: ${{ steps.check-secrets.outputs.has_secrets }} steps: - name: Check out repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -70,6 +67,14 @@ jobs: NODE_VERSION=${NODE_NVMRC/v/''} echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT + - name: Check secrets + id: check-secrets + env: + AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + run: | + has_secrets=${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL != '' }} + echo "has_secrets=$has_secrets" >> $GITHUB_OUTPUT + build-artifacts: name: Build artifacts runs-on: ubuntu-22.04 @@ -128,7 +133,7 @@ jobs: run: npm ci - name: Download SDK Artifacts - if: ${{ inputs.sdk_branch != '' }} + if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }} uses: bitwarden/gh-actions/download-artifacts@main with: github_token: ${{secrets.GITHUB_TOKEN}} @@ -141,7 +146,7 @@ jobs: if_no_artifact_found: fail - name: Override SDK - if: ${{ inputs.sdk_branch != '' }} + if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }} working-directory: ./ run: | ls -l ../ @@ -210,19 +215,23 @@ jobs: ########## ACRs ########## - name: Login to Prod Azure + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 with: creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }} - name: Log into Prod container registry + if: ${{ needs.setup.outputs.has_secrets == 'true' }} run: az acr login -n bitwardenprod - name: Login to Azure - CI Subscription + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve github PAT secrets + if: ${{ needs.setup.outputs.has_secrets == 'true' }} id: retrieve-secret-pat uses: bitwarden/gh-actions/get-keyvault-secrets@main with: @@ -270,6 +279,7 @@ jobs: run: echo "name=$_AZ_REGISTRY/${PROJECT_NAME}:${IMAGE_TAG}" >> $GITHUB_OUTPUT - name: Build Docker image + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0 with: context: apps/web diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9dc72c7fdda..1b738bd7bcf 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,12 +1,20 @@ name: Lint on: - push: + pull_request: + types: [opened, synchronize] branches-ignore: - 'l10n_master' - 'cf-pages' paths-ignore: - '.github/workflows/**' + push: + branches: + - 'main' + - 'rc' + - 'hotfix-rc-*' + paths-ignore: + - '.github/workflows/**' workflow_dispatch: inputs: {}