1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-28 12:45:45 +01:00

[EC-243] Grant premium status when member accepts org invite (#2870)

* Rename setCanAccessPremium to setHasPremiumPersonally

* Add hasPremiumFromOrganization

* Refactor stateService methods

* Add getHasPremiumPersonally, deprecate tokenService.getPremium
This commit is contained in:
Thomas Rittson 2022-06-14 12:13:41 +10:00 committed by GitHub
parent 29f95cdb7d
commit b92685dcd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 52 additions and 27 deletions

View File

@ -45,8 +45,8 @@ export class PremiumComponent implements OnInit {
async ngOnInit() {
this.canAccessPremium = await this.stateService.getCanAccessPremium();
const premium = await this.tokenService.getPremium();
if (premium) {
const premiumPersonally = await this.stateService.getHasPremiumPersonally();
if (premiumPersonally) {
this.router.navigate(["/settings/subscription/user-subscription"]);
return;
}

View File

@ -51,9 +51,9 @@ export class SettingsComponent implements OnInit, OnDestroy {
}
async load() {
this.premium = await this.tokenService.getPremium();
this.premium = await this.stateService.getHasPremiumPersonally();
this.hasFamilySponsorshipAvailable = await this.organizationService.canManageSponsorships();
const hasPremiumFromOrg = await this.stateService.getCanAccessPremium();
const hasPremiumFromOrg = await this.stateService.getHasPremiumFromOrganization();
let billing = null;
if (!this.selfHosted) {
billing = await this.apiService.getUserBillingHistory();

View File

@ -1,7 +1,7 @@
import { Component } from "@angular/core";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { TokenService } from "jslib-common/abstractions/token.service";
import { StateService } from "jslib-common/abstractions/state.service";
@Component({
selector: "app-subscription",
@ -12,12 +12,12 @@ export class SubscriptionComponent {
selfHosted: boolean;
constructor(
private tokenService: TokenService,
private stateService: StateService,
private platformUtilsService: PlatformUtilsService
) {}
async ngOnInit() {
this.hasPremium = await this.tokenService.getPremium();
this.hasPremium = await this.stateService.getHasPremiumPersonally();
this.selfHosted = this.platformUtilsService.isSelfHost();
}

View File

@ -5,7 +5,7 @@ import { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { TokenService } from "jslib-common/abstractions/token.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { SubscriptionResponse } from "jslib-common/models/response/subscriptionResponse";
@Component({
@ -25,7 +25,7 @@ export class UserSubscriptionComponent implements OnInit {
reinstatePromise: Promise<any>;
constructor(
private tokenService: TokenService,
private stateService: StateService,
private apiService: ApiService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
@ -45,7 +45,7 @@ export class UserSubscriptionComponent implements OnInit {
return;
}
if (this.tokenService.getPremium()) {
if (this.stateService.getHasPremiumPersonally()) {
this.loading = true;
this.sub = await this.apiService.getUserSubscription();
} else {

View File

@ -58,7 +58,10 @@ export abstract class StateService<T extends Account = Account> {
getBiometricUnlock: (options?: StorageOptions) => Promise<boolean>;
setBiometricUnlock: (value: boolean, options?: StorageOptions) => Promise<void>;
getCanAccessPremium: (options?: StorageOptions) => Promise<boolean>;
setCanAccessPremium: (value: boolean, options?: StorageOptions) => Promise<void>;
getHasPremiumPersonally: (options?: StorageOptions) => Promise<boolean>;
setHasPremiumPersonally: (value: boolean, options?: StorageOptions) => Promise<void>;
setHasPremiumFromOrganization: (value: boolean, options?: StorageOptions) => Promise<void>;
getHasPremiumFromOrganization: (options?: StorageOptions) => Promise<boolean>;
getClearClipboard: (options?: StorageOptions) => Promise<number>;
setClearClipboard: (value: number, options?: StorageOptions) => Promise<void>;
getCollapsedGroupings: (options?: StorageOptions) => Promise<string[]>;

View File

@ -26,7 +26,6 @@ export abstract class TokenService {
getEmail: () => Promise<string>;
getEmailVerified: () => Promise<boolean>;
getName: () => Promise<string>;
getPremium: () => Promise<boolean>;
getIssuer: () => Promise<string>;
getIsExternal: () => Promise<boolean>;
}

View File

@ -90,6 +90,7 @@ export class AccountProfile {
everBeenUnlocked?: boolean;
forcePasswordReset?: boolean;
hasPremiumPersonally?: boolean;
hasPremiumFromOrganization?: boolean;
lastSync?: string;
userId?: string;
usesKeyConnector?: boolean;

View File

@ -9,7 +9,8 @@ export class ProfileResponse extends BaseResponse {
email: string;
emailVerified: boolean;
masterPasswordHint: string;
premium: boolean;
premiumPersonally: boolean;
premiumFromOrganization: boolean;
culture: string;
twoFactorEnabled: boolean;
key: string;
@ -28,7 +29,8 @@ export class ProfileResponse extends BaseResponse {
this.email = this.getResponseProperty("Email");
this.emailVerified = this.getResponseProperty("EmailVerified");
this.masterPasswordHint = this.getResponseProperty("MasterPasswordHint");
this.premium = this.getResponseProperty("Premium");
this.premiumPersonally = this.getResponseProperty("Premium");
this.premiumFromOrganization = this.getResponseProperty("PremiumFromOrganization");
this.culture = this.getResponseProperty("Culture");
this.twoFactorEnabled = this.getResponseProperty("TwoFactorEnabled");
this.key = this.getResponseProperty("Key");

View File

@ -337,13 +337,41 @@ export class StateService<
return false;
}
return (
(await this.getHasPremiumPersonally(options)) ||
(await this.getHasPremiumFromOrganization(options))
);
}
async getHasPremiumPersonally(options?: StorageOptions): Promise<boolean> {
const account = await this.getAccount(
this.reconcileOptions(options, await this.defaultOnDiskOptions())
);
if (account.profile.hasPremiumPersonally) {
return account?.profile?.hasPremiumPersonally;
}
async setHasPremiumPersonally(value: boolean, options?: StorageOptions): Promise<void> {
const account = await this.getAccount(
this.reconcileOptions(options, await this.defaultOnDiskOptions())
);
account.profile.hasPremiumPersonally = value;
await this.saveAccount(
account,
this.reconcileOptions(options, await this.defaultOnDiskOptions())
);
}
async getHasPremiumFromOrganization(options?: StorageOptions): Promise<boolean> {
const account = await this.getAccount(
this.reconcileOptions(options, await this.defaultOnDiskOptions())
);
if (account.profile?.hasPremiumFromOrganization) {
return true;
}
// TODO: older server versions won't send the hasPremiumFromOrganization flag, so we're keeping the old logic
// for backwards compatibility. It can be removed after everyone has upgraded.
const organizations = await this.getOrganizations(options);
if (organizations == null) {
return false;
@ -359,11 +387,11 @@ export class StateService<
return false;
}
async setCanAccessPremium(value: boolean, options?: StorageOptions): Promise<void> {
async setHasPremiumFromOrganization(value: boolean, options?: StorageOptions): Promise<void> {
const account = await this.getAccount(
this.reconcileOptions(options, await this.defaultOnDiskOptions())
);
account.profile.hasPremiumPersonally = value;
account.profile.hasPremiumFromOrganization = value;
await this.saveAccount(
account,
this.reconcileOptions(options, await this.defaultOnDiskOptions())

View File

@ -304,7 +304,8 @@ export class SyncService implements SyncServiceAbstraction {
await this.cryptoService.setOrgKeys(response.organizations, response.providerOrganizations);
await this.stateService.setSecurityStamp(response.securityStamp);
await this.stateService.setEmailVerified(response.emailVerified);
await this.stateService.setCanAccessPremium(response.premium);
await this.stateService.setHasPremiumPersonally(response.premiumPersonally);
await this.stateService.setHasPremiumFromOrganization(response.premiumFromOrganization);
await this.stateService.setForcePasswordReset(response.forcePasswordReset);
await this.keyConnectorService.setUsesKeyConnector(response.usesKeyConnector);

View File

@ -169,15 +169,6 @@ export class TokenService implements TokenServiceAbstraction {
return decoded.name as string;
}
async getPremium(): Promise<boolean> {
const decoded = await this.decodeToken();
if (typeof decoded.premium === "undefined") {
return false;
}
return decoded.premium as boolean;
}
async getIssuer(): Promise<string> {
const decoded = await this.decodeToken();
if (typeof decoded.iss === "undefined") {