[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": {
|
||||
"message": "Autofill on page load?"
|
||||
},
|
||||
"cardExpiredTitle": {
|
||||
"message": "Expired card"
|
||||
},
|
||||
"cardExpiredMessage": {
|
||||
"message": "If you've renewed it, update the card's information"
|
||||
},
|
||||
"cardDetails": {
|
||||
"message": "Card details"
|
||||
},
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
<bit-callout type="info" *ngIf="allowOwnershipAssignment() && !allowPersonal">
|
||||
{{ "personalOwnershipPolicyInEffect" | i18n }}
|
||||
</bit-callout>
|
||||
<bit-callout *ngIf="cardIsExpired" type="info" [title]="'cardExpiredTitle' | i18n">
|
||||
{{ "cardExpiredMessage" | i18n }}
|
||||
</bit-callout>
|
||||
<div class="row" *ngIf="!editMode && !viewOnly">
|
||||
<div class="col-6 form-group">
|
||||
<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 { ProductTierType } from "@bitwarden/common/billing/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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.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 { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { Launchable } from "@bitwarden/common/vault/interfaces/launchable";
|
||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||
import { PasswordRepromptService } from "@bitwarden/vault";
|
||||
|
@ -43,6 +45,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
|||
viewingPasswordHistory = false;
|
||||
viewOnly = false;
|
||||
showPasswordCount = false;
|
||||
cardIsExpired: boolean = false;
|
||||
|
||||
protected totpInterval: number;
|
||||
protected override componentName = "app-vault-add-edit";
|
||||
|
@ -115,6 +118,12 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
|||
await this.totpTick(interval);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const extensionRefreshEnabled = await firstValueFrom(
|
||||
this.configService.getFeatureFlag$(FeatureFlag.ExtensionRefresh),
|
||||
);
|
||||
|
||||
this.cardIsExpired = extensionRefreshEnabled && this.isCardExpiryInThePast();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
@ -226,6 +235,24 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
|||
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() {
|
||||
if (this.totpInterval) {
|
||||
window.clearInterval(this.totpInterval);
|
||||
|
|
|
@ -194,6 +194,12 @@
|
|||
"dr": {
|
||||
"message": "Dr"
|
||||
},
|
||||
"cardExpiredTitle": {
|
||||
"message": "Expired card"
|
||||
},
|
||||
"cardExpiredMessage": {
|
||||
"message": "If you've renewed it, update the card's information"
|
||||
},
|
||||
"expirationMonth": {
|
||||
"message": "Expiration month"
|
||||
},
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
<ng-container *ngIf="!!cipher">
|
||||
<bit-callout *ngIf="cardIsExpired" type="info" [title]="'cardExpiredTitle' | i18n">
|
||||
{{ "cardExpiredMessage" | i18n }}
|
||||
</bit-callout>
|
||||
|
||||
<!-- ITEM DETAILS -->
|
||||
<app-item-details-v2
|
||||
[cipher]="cipher"
|
||||
|
|
|
@ -8,10 +8,11 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
|
|||
import { CollectionId } from "@bitwarden/common/types/guid";
|
||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||
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 { CollectionView } from "@bitwarden/common/vault/models/view/collection.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 { AttachmentsV2ViewComponent } from "./attachments/attachments-v2-view.component";
|
||||
|
@ -28,6 +29,7 @@ import { ViewIdentitySectionsComponent } from "./view-identity-sections/view-ide
|
|||
templateUrl: "cipher-view.component.html",
|
||||
standalone: true,
|
||||
imports: [
|
||||
CalloutModule,
|
||||
CommonModule,
|
||||
SearchModule,
|
||||
JslibModule,
|
||||
|
@ -48,6 +50,7 @@ export class CipherViewComponent implements OnInit, OnDestroy {
|
|||
folder$: Observable<FolderView>;
|
||||
collections$: Observable<CollectionView[]>;
|
||||
private destroyed$: Subject<void> = new Subject();
|
||||
cardIsExpired: boolean = false;
|
||||
|
||||
constructor(
|
||||
private organizationService: OrganizationService,
|
||||
|
@ -57,6 +60,8 @@ export class CipherViewComponent implements OnInit, OnDestroy {
|
|||
|
||||
async ngOnInit() {
|
||||
await this.loadCipherData();
|
||||
|
||||
this.cardIsExpired = this.isCardExpiryInThePast();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
@ -97,4 +102,24 @@ export class CipherViewComponent implements OnInit, OnDestroy {
|
|||
.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