mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-24 12:06:15 +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:
parent
29f95cdb7d
commit
b92685dcd9
@ -45,8 +45,8 @@ export class PremiumComponent implements OnInit {
|
|||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.canAccessPremium = await this.stateService.getCanAccessPremium();
|
this.canAccessPremium = await this.stateService.getCanAccessPremium();
|
||||||
const premium = await this.tokenService.getPremium();
|
const premiumPersonally = await this.stateService.getHasPremiumPersonally();
|
||||||
if (premium) {
|
if (premiumPersonally) {
|
||||||
this.router.navigate(["/settings/subscription/user-subscription"]);
|
this.router.navigate(["/settings/subscription/user-subscription"]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -51,9 +51,9 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
this.premium = await this.tokenService.getPremium();
|
this.premium = await this.stateService.getHasPremiumPersonally();
|
||||||
this.hasFamilySponsorshipAvailable = await this.organizationService.canManageSponsorships();
|
this.hasFamilySponsorshipAvailable = await this.organizationService.canManageSponsorships();
|
||||||
const hasPremiumFromOrg = await this.stateService.getCanAccessPremium();
|
const hasPremiumFromOrg = await this.stateService.getHasPremiumFromOrganization();
|
||||||
let billing = null;
|
let billing = null;
|
||||||
if (!this.selfHosted) {
|
if (!this.selfHosted) {
|
||||||
billing = await this.apiService.getUserBillingHistory();
|
billing = await this.apiService.getUserBillingHistory();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.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";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-subscription",
|
selector: "app-subscription",
|
||||||
@ -12,12 +12,12 @@ export class SubscriptionComponent {
|
|||||||
selfHosted: boolean;
|
selfHosted: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private tokenService: TokenService,
|
private stateService: StateService,
|
||||||
private platformUtilsService: PlatformUtilsService
|
private platformUtilsService: PlatformUtilsService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.hasPremium = await this.tokenService.getPremium();
|
this.hasPremium = await this.stateService.getHasPremiumPersonally();
|
||||||
this.selfHosted = this.platformUtilsService.isSelfHost();
|
this.selfHosted = this.platformUtilsService.isSelfHost();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { ApiService } from "jslib-common/abstractions/api.service";
|
|||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from "jslib-common/abstractions/log.service";
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.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";
|
import { SubscriptionResponse } from "jslib-common/models/response/subscriptionResponse";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -25,7 +25,7 @@ export class UserSubscriptionComponent implements OnInit {
|
|||||||
reinstatePromise: Promise<any>;
|
reinstatePromise: Promise<any>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private tokenService: TokenService,
|
private stateService: StateService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
@ -45,7 +45,7 @@ export class UserSubscriptionComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.tokenService.getPremium()) {
|
if (this.stateService.getHasPremiumPersonally()) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.sub = await this.apiService.getUserSubscription();
|
this.sub = await this.apiService.getUserSubscription();
|
||||||
} else {
|
} else {
|
||||||
|
@ -58,7 +58,10 @@ export abstract class StateService<T extends Account = Account> {
|
|||||||
getBiometricUnlock: (options?: StorageOptions) => Promise<boolean>;
|
getBiometricUnlock: (options?: StorageOptions) => Promise<boolean>;
|
||||||
setBiometricUnlock: (value: boolean, options?: StorageOptions) => Promise<void>;
|
setBiometricUnlock: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||||
getCanAccessPremium: (options?: StorageOptions) => Promise<boolean>;
|
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>;
|
getClearClipboard: (options?: StorageOptions) => Promise<number>;
|
||||||
setClearClipboard: (value: number, options?: StorageOptions) => Promise<void>;
|
setClearClipboard: (value: number, options?: StorageOptions) => Promise<void>;
|
||||||
getCollapsedGroupings: (options?: StorageOptions) => Promise<string[]>;
|
getCollapsedGroupings: (options?: StorageOptions) => Promise<string[]>;
|
||||||
|
@ -26,7 +26,6 @@ export abstract class TokenService {
|
|||||||
getEmail: () => Promise<string>;
|
getEmail: () => Promise<string>;
|
||||||
getEmailVerified: () => Promise<boolean>;
|
getEmailVerified: () => Promise<boolean>;
|
||||||
getName: () => Promise<string>;
|
getName: () => Promise<string>;
|
||||||
getPremium: () => Promise<boolean>;
|
|
||||||
getIssuer: () => Promise<string>;
|
getIssuer: () => Promise<string>;
|
||||||
getIsExternal: () => Promise<boolean>;
|
getIsExternal: () => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,7 @@ export class AccountProfile {
|
|||||||
everBeenUnlocked?: boolean;
|
everBeenUnlocked?: boolean;
|
||||||
forcePasswordReset?: boolean;
|
forcePasswordReset?: boolean;
|
||||||
hasPremiumPersonally?: boolean;
|
hasPremiumPersonally?: boolean;
|
||||||
|
hasPremiumFromOrganization?: boolean;
|
||||||
lastSync?: string;
|
lastSync?: string;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
usesKeyConnector?: boolean;
|
usesKeyConnector?: boolean;
|
||||||
|
@ -9,7 +9,8 @@ export class ProfileResponse extends BaseResponse {
|
|||||||
email: string;
|
email: string;
|
||||||
emailVerified: boolean;
|
emailVerified: boolean;
|
||||||
masterPasswordHint: string;
|
masterPasswordHint: string;
|
||||||
premium: boolean;
|
premiumPersonally: boolean;
|
||||||
|
premiumFromOrganization: boolean;
|
||||||
culture: string;
|
culture: string;
|
||||||
twoFactorEnabled: boolean;
|
twoFactorEnabled: boolean;
|
||||||
key: string;
|
key: string;
|
||||||
@ -28,7 +29,8 @@ export class ProfileResponse extends BaseResponse {
|
|||||||
this.email = this.getResponseProperty("Email");
|
this.email = this.getResponseProperty("Email");
|
||||||
this.emailVerified = this.getResponseProperty("EmailVerified");
|
this.emailVerified = this.getResponseProperty("EmailVerified");
|
||||||
this.masterPasswordHint = this.getResponseProperty("MasterPasswordHint");
|
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.culture = this.getResponseProperty("Culture");
|
||||||
this.twoFactorEnabled = this.getResponseProperty("TwoFactorEnabled");
|
this.twoFactorEnabled = this.getResponseProperty("TwoFactorEnabled");
|
||||||
this.key = this.getResponseProperty("Key");
|
this.key = this.getResponseProperty("Key");
|
||||||
|
@ -337,13 +337,41 @@ export class StateService<
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
(await this.getHasPremiumPersonally(options)) ||
|
||||||
|
(await this.getHasPremiumFromOrganization(options))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getHasPremiumPersonally(options?: StorageOptions): Promise<boolean> {
|
||||||
const account = await this.getAccount(
|
const account = await this.getAccount(
|
||||||
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
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;
|
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);
|
const organizations = await this.getOrganizations(options);
|
||||||
if (organizations == null) {
|
if (organizations == null) {
|
||||||
return false;
|
return false;
|
||||||
@ -359,11 +387,11 @@ export class StateService<
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async setCanAccessPremium(value: boolean, options?: StorageOptions): Promise<void> {
|
async setHasPremiumFromOrganization(value: boolean, options?: StorageOptions): Promise<void> {
|
||||||
const account = await this.getAccount(
|
const account = await this.getAccount(
|
||||||
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
||||||
);
|
);
|
||||||
account.profile.hasPremiumPersonally = value;
|
account.profile.hasPremiumFromOrganization = value;
|
||||||
await this.saveAccount(
|
await this.saveAccount(
|
||||||
account,
|
account,
|
||||||
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
||||||
|
@ -304,7 +304,8 @@ export class SyncService implements SyncServiceAbstraction {
|
|||||||
await this.cryptoService.setOrgKeys(response.organizations, response.providerOrganizations);
|
await this.cryptoService.setOrgKeys(response.organizations, response.providerOrganizations);
|
||||||
await this.stateService.setSecurityStamp(response.securityStamp);
|
await this.stateService.setSecurityStamp(response.securityStamp);
|
||||||
await this.stateService.setEmailVerified(response.emailVerified);
|
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.stateService.setForcePasswordReset(response.forcePasswordReset);
|
||||||
await this.keyConnectorService.setUsesKeyConnector(response.usesKeyConnector);
|
await this.keyConnectorService.setUsesKeyConnector(response.usesKeyConnector);
|
||||||
|
|
||||||
|
@ -169,15 +169,6 @@ export class TokenService implements TokenServiceAbstraction {
|
|||||||
return decoded.name as string;
|
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> {
|
async getIssuer(): Promise<string> {
|
||||||
const decoded = await this.decodeToken();
|
const decoded = await this.decodeToken();
|
||||||
if (typeof decoded.iss === "undefined") {
|
if (typeof decoded.iss === "undefined") {
|
||||||
|
Loading…
Reference in New Issue
Block a user