mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-14 10:26:19 +01:00
[BEEEP][PM-3876] - Warn users if a stored card has an expiry date in the past (#10470)
* add enableExpiredPaymentCipherWarning setting * add card expiry warning to the v2 Card Details component * remove enableExpiredPaymentCipherWarning setting * update expired card callout design and copy * move card expired callout to cipher view * add card expiry warning to the web vault add-edit cipher component
This commit is contained in:
parent
b030c6e27b
commit
1fe6631c82
@ -3960,6 +3960,12 @@
|
|||||||
"autoFillOnPageLoad": {
|
"autoFillOnPageLoad": {
|
||||||
"message": "Autofill on page load?"
|
"message": "Autofill on page load?"
|
||||||
},
|
},
|
||||||
|
"cardExpiredTitle": {
|
||||||
|
"message": "Expired card"
|
||||||
|
},
|
||||||
|
"cardExpiredMessage": {
|
||||||
|
"message": "If you've renewed it, update the card's information"
|
||||||
|
},
|
||||||
"cardDetails": {
|
"cardDetails": {
|
||||||
"message": "Card details"
|
"message": "Card details"
|
||||||
},
|
},
|
||||||
|
@ -23,6 +23,9 @@
|
|||||||
<bit-callout type="info" *ngIf="allowOwnershipAssignment() && !allowPersonal">
|
<bit-callout type="info" *ngIf="allowOwnershipAssignment() && !allowPersonal">
|
||||||
{{ "personalOwnershipPolicyInEffect" | i18n }}
|
{{ "personalOwnershipPolicyInEffect" | i18n }}
|
||||||
</bit-callout>
|
</bit-callout>
|
||||||
|
<bit-callout *ngIf="cardIsExpired" type="info" [title]="'cardExpiredTitle' | i18n">
|
||||||
|
{{ "cardExpiredMessage" | i18n }}
|
||||||
|
</bit-callout>
|
||||||
<div class="row" *ngIf="!editMode && !viewOnly">
|
<div class="row" *ngIf="!editMode && !viewOnly">
|
||||||
<div class="col-6 form-group">
|
<div class="col-6 form-group">
|
||||||
<label for="type">{{ "whatTypeOfItem" | i18n }}</label>
|
<label for="type">{{ "whatTypeOfItem" | i18n }}</label>
|
||||||
|
@ -11,6 +11,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
|
|||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
import { EventType } from "@bitwarden/common/enums";
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
@ -23,6 +24,7 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde
|
|||||||
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
import { Launchable } from "@bitwarden/common/vault/interfaces/launchable";
|
import { Launchable } from "@bitwarden/common/vault/interfaces/launchable";
|
||||||
|
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||||
import { PasswordRepromptService } from "@bitwarden/vault";
|
import { PasswordRepromptService } from "@bitwarden/vault";
|
||||||
@ -43,6 +45,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
|||||||
viewingPasswordHistory = false;
|
viewingPasswordHistory = false;
|
||||||
viewOnly = false;
|
viewOnly = false;
|
||||||
showPasswordCount = false;
|
showPasswordCount = false;
|
||||||
|
cardIsExpired: boolean = false;
|
||||||
|
|
||||||
protected totpInterval: number;
|
protected totpInterval: number;
|
||||||
protected override componentName = "app-vault-add-edit";
|
protected override componentName = "app-vault-add-edit";
|
||||||
@ -115,6 +118,12 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
|||||||
await this.totpTick(interval);
|
await this.totpTick(interval);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const extensionRefreshEnabled = await firstValueFrom(
|
||||||
|
this.configService.getFeatureFlag$(FeatureFlag.ExtensionRefresh),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.cardIsExpired = extensionRefreshEnabled && this.isCardExpiryInThePast();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@ -226,6 +235,24 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
|||||||
this.viewingPasswordHistory = !this.viewingPasswordHistory;
|
this.viewingPasswordHistory = !this.viewingPasswordHistory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isCardExpiryInThePast() {
|
||||||
|
if (this.cipher.card) {
|
||||||
|
const { expMonth, expYear }: CardView = this.cipher.card;
|
||||||
|
|
||||||
|
if (expYear && expMonth) {
|
||||||
|
// `Date` months are zero-indexed
|
||||||
|
const parsedMonth = parseInt(expMonth) - 1;
|
||||||
|
const parsedYear = parseInt(expYear);
|
||||||
|
|
||||||
|
// First day of the next month minus one, to get last day of the card month
|
||||||
|
const cardExpiry = new Date(parsedYear, parsedMonth + 1, 0);
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
return cardExpiry < now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected cleanUp() {
|
protected cleanUp() {
|
||||||
if (this.totpInterval) {
|
if (this.totpInterval) {
|
||||||
window.clearInterval(this.totpInterval);
|
window.clearInterval(this.totpInterval);
|
||||||
|
@ -194,6 +194,12 @@
|
|||||||
"dr": {
|
"dr": {
|
||||||
"message": "Dr"
|
"message": "Dr"
|
||||||
},
|
},
|
||||||
|
"cardExpiredTitle": {
|
||||||
|
"message": "Expired card"
|
||||||
|
},
|
||||||
|
"cardExpiredMessage": {
|
||||||
|
"message": "If you've renewed it, update the card's information"
|
||||||
|
},
|
||||||
"expirationMonth": {
|
"expirationMonth": {
|
||||||
"message": "Expiration month"
|
"message": "Expiration month"
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
<ng-container *ngIf="!!cipher">
|
<ng-container *ngIf="!!cipher">
|
||||||
|
<bit-callout *ngIf="cardIsExpired" type="info" [title]="'cardExpiredTitle' | i18n">
|
||||||
|
{{ "cardExpiredMessage" | i18n }}
|
||||||
|
</bit-callout>
|
||||||
|
|
||||||
<!-- ITEM DETAILS -->
|
<!-- ITEM DETAILS -->
|
||||||
<app-item-details-v2
|
<app-item-details-v2
|
||||||
[cipher]="cipher"
|
[cipher]="cipher"
|
||||||
|
@ -8,10 +8,11 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
|
|||||||
import { CollectionId } from "@bitwarden/common/types/guid";
|
import { CollectionId } from "@bitwarden/common/types/guid";
|
||||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||||
|
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
||||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||||
import { SearchModule } from "@bitwarden/components";
|
import { SearchModule, CalloutModule } from "@bitwarden/components";
|
||||||
|
|
||||||
import { AdditionalOptionsComponent } from "./additional-options/additional-options.component";
|
import { AdditionalOptionsComponent } from "./additional-options/additional-options.component";
|
||||||
import { AttachmentsV2ViewComponent } from "./attachments/attachments-v2-view.component";
|
import { AttachmentsV2ViewComponent } from "./attachments/attachments-v2-view.component";
|
||||||
@ -28,6 +29,7 @@ import { ViewIdentitySectionsComponent } from "./view-identity-sections/view-ide
|
|||||||
templateUrl: "cipher-view.component.html",
|
templateUrl: "cipher-view.component.html",
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
|
CalloutModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
SearchModule,
|
SearchModule,
|
||||||
JslibModule,
|
JslibModule,
|
||||||
@ -48,6 +50,7 @@ export class CipherViewComponent implements OnInit, OnDestroy {
|
|||||||
folder$: Observable<FolderView>;
|
folder$: Observable<FolderView>;
|
||||||
collections$: Observable<CollectionView[]>;
|
collections$: Observable<CollectionView[]>;
|
||||||
private destroyed$: Subject<void> = new Subject();
|
private destroyed$: Subject<void> = new Subject();
|
||||||
|
cardIsExpired: boolean = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private organizationService: OrganizationService,
|
private organizationService: OrganizationService,
|
||||||
@ -57,6 +60,8 @@ export class CipherViewComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
await this.loadCipherData();
|
await this.loadCipherData();
|
||||||
|
|
||||||
|
this.cardIsExpired = this.isCardExpiryInThePast();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
@ -97,4 +102,24 @@ export class CipherViewComponent implements OnInit, OnDestroy {
|
|||||||
.pipe(takeUntil(this.destroyed$));
|
.pipe(takeUntil(this.destroyed$));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isCardExpiryInThePast() {
|
||||||
|
if (this.cipher.card) {
|
||||||
|
const { expMonth, expYear }: CardView = this.cipher.card;
|
||||||
|
|
||||||
|
if (expYear && expMonth) {
|
||||||
|
// `Date` months are zero-indexed
|
||||||
|
const parsedMonth = parseInt(expMonth) - 1;
|
||||||
|
const parsedYear = parseInt(expYear);
|
||||||
|
|
||||||
|
// First day of the next month minus one, to get last day of the card month
|
||||||
|
const cardExpiry = new Date(parsedYear, parsedMonth + 1, 0);
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
return cardExpiry < now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user