diff --git a/common/src/abstractions/api.service.ts b/common/src/abstractions/api.service.ts index 384a983535..06eb39e729 100644 --- a/common/src/abstractions/api.service.ts +++ b/common/src/abstractions/api.service.ts @@ -32,6 +32,8 @@ import { ImportOrganizationCiphersRequest } from '../models/request/importOrgani import { KdfRequest } from '../models/request/kdfRequest'; import { KeyConnectorUserKeyRequest } from '../models/request/keyConnectorUserKeyRequest'; import { KeysRequest } from '../models/request/keysRequest'; +import { OrganizationSponsorshipCreateRequest } from '../models/request/organization/organizationSponsorshipCreateRequest'; +import { OrganizationSponsorshipRedeemRequest } from '../models/request/organization/organizationSponsorshipRedeemRequest'; import { OrganizationSsoRequest } from '../models/request/organization/organizationSsoRequest'; import { OrganizationCreateRequest } from '../models/request/organizationCreateRequest'; import { OrganizationImportRequest } from '../models/request/organizationImportRequest'; @@ -453,6 +455,12 @@ export abstract class ApiService { preValidateSso: (identifier: string) => Promise; + postCreateSponsorship: (sponsorshipOrgId: string, request: OrganizationSponsorshipCreateRequest) => Promise; + deleteRevokeSponsorship: (sponsoringOrganizationId: string) => Promise; + deleteRemoveSponsorship: (sponsoringOrgId: string) => Promise; + postRedeemSponsorship: (sponsorshipToken: string, request: OrganizationSponsorshipRedeemRequest) => Promise; + postResendSponsorshipOffer: (sponsoringOrgId: string) => Promise; + getUserKeyFromKeyConnector: (keyConnectorUrl: string) => Promise; postUserKeyToKeyConnector: (keyConnectorUrl: string, request: KeyConnectorUserKeyRequest) => Promise; getKeyConnectorAlive: (keyConnectorUrl: string) => Promise; diff --git a/common/src/abstractions/user.service.ts b/common/src/abstractions/user.service.ts index 81c3da873d..8170ae73a7 100644 --- a/common/src/abstractions/user.service.ts +++ b/common/src/abstractions/user.service.ts @@ -21,6 +21,7 @@ export abstract class UserService { clear: () => Promise; isAuthenticated: () => Promise; canAccessPremium: () => Promise; + canManageSponsorships: () => Promise; getOrganization: (id: string) => Promise; getOrganizationByIdentifier: (identifier: string) => Promise; getAllOrganizations: () => Promise; diff --git a/common/src/enums/planSponsorshipType.ts b/common/src/enums/planSponsorshipType.ts new file mode 100644 index 0000000000..330b7ec027 --- /dev/null +++ b/common/src/enums/planSponsorshipType.ts @@ -0,0 +1,3 @@ +export enum PlanSponsorshipType { + FamiliesForEnterprise = 0, +} diff --git a/common/src/models/data/organizationData.ts b/common/src/models/data/organizationData.ts index 6c7a42b111..c40610571e 100644 --- a/common/src/models/data/organizationData.ts +++ b/common/src/models/data/organizationData.ts @@ -2,6 +2,8 @@ import { ProfileOrganizationResponse } from '../response/profileOrganizationResp import { OrganizationUserStatusType } from '../../enums/organizationUserStatusType'; import { OrganizationUserType } from '../../enums/organizationUserType'; +import { ProductType } from '../../enums/productType'; + import { PermissionsApi } from '../api/permissionsApi'; export class OrganizationData { @@ -34,6 +36,9 @@ export class OrganizationData { providerId: string; providerName: string; isProviderUser: boolean; + familySponsorshipFriendlyName: string; + familySponsorshipAvailable: boolean; + planProductType: ProductType; keyConnectorEnabled: boolean; keyConnectorUrl: string; @@ -66,6 +71,9 @@ export class OrganizationData { this.hasPublicAndPrivateKeys = response.hasPublicAndPrivateKeys; this.providerId = response.providerId; this.providerName = response.providerName; + this.familySponsorshipFriendlyName = response.familySponsorshipFriendlyName; + this.familySponsorshipAvailable = response.familySponsorshipAvailable; + this.planProductType = response.planProductType; this.keyConnectorEnabled = response.keyConnectorEnabled; this.keyConnectorUrl = response.keyConnectorUrl; } diff --git a/common/src/models/domain/organization.ts b/common/src/models/domain/organization.ts index 4a51241d3b..492afda50a 100644 --- a/common/src/models/domain/organization.ts +++ b/common/src/models/domain/organization.ts @@ -2,6 +2,7 @@ import { OrganizationData } from '../data/organizationData'; import { OrganizationUserStatusType } from '../../enums/organizationUserStatusType'; import { OrganizationUserType } from '../../enums/organizationUserType'; +import { ProductType } from '../../enums/productType'; import { PermissionsApi } from '../api/permissionsApi'; @@ -35,6 +36,9 @@ export class Organization { providerId: string; providerName: string; isProviderUser: boolean; + familySponsorshipFriendlyName: string; + familySponsorshipAvailable: boolean; + planProductType: ProductType; keyConnectorEnabled: boolean; keyConnectorUrl: string; @@ -72,6 +76,9 @@ export class Organization { this.providerId = obj.providerId; this.providerName = obj.providerName; this.isProviderUser = obj.isProviderUser; + this.familySponsorshipFriendlyName = obj.familySponsorshipFriendlyName; + this.familySponsorshipAvailable = obj.familySponsorshipAvailable; + this.planProductType = obj.planProductType; this.keyConnectorEnabled = obj.keyConnectorEnabled; this.keyConnectorUrl = obj.keyConnectorUrl; } diff --git a/common/src/models/request/organization/organizationSponsorshipCreateRequest.ts b/common/src/models/request/organization/organizationSponsorshipCreateRequest.ts new file mode 100644 index 0000000000..6dd8c267e3 --- /dev/null +++ b/common/src/models/request/organization/organizationSponsorshipCreateRequest.ts @@ -0,0 +1,7 @@ +import { PlanSponsorshipType } from '../../../enums/planSponsorshipType'; + +export class OrganizationSponsorshipCreateRequest { + sponsoredEmail: string; + planSponsorshipType: PlanSponsorshipType; + friendlyName: string; +} diff --git a/common/src/models/request/organization/organizationSponsorshipRedeemRequest.ts b/common/src/models/request/organization/organizationSponsorshipRedeemRequest.ts new file mode 100644 index 0000000000..7f325ea186 --- /dev/null +++ b/common/src/models/request/organization/organizationSponsorshipRedeemRequest.ts @@ -0,0 +1,6 @@ +import { PlanSponsorshipType } from '../../../enums/planSponsorshipType'; + +export class OrganizationSponsorshipRedeemRequest { + planSponsorshipType: PlanSponsorshipType; + sponsoredOrganizationId: string; +} diff --git a/common/src/models/response/profileOrganizationResponse.ts b/common/src/models/response/profileOrganizationResponse.ts index 8b71e799b6..fdd873baed 100644 --- a/common/src/models/response/profileOrganizationResponse.ts +++ b/common/src/models/response/profileOrganizationResponse.ts @@ -2,6 +2,7 @@ import { BaseResponse } from './baseResponse'; import { OrganizationUserStatusType } from '../../enums/organizationUserStatusType'; import { OrganizationUserType } from '../../enums/organizationUserType'; +import { ProductType } from '../../enums/productType'; import { PermissionsApi } from '../api/permissionsApi'; export class ProfileOrganizationResponse extends BaseResponse { @@ -34,6 +35,9 @@ export class ProfileOrganizationResponse extends BaseResponse { userId: string; providerId: string; providerName: string; + familySponsorshipFriendlyName: string; + familySponsorshipAvailable: boolean; + planProductType: ProductType; keyConnectorEnabled: boolean; keyConnectorUrl: string; @@ -68,6 +72,9 @@ export class ProfileOrganizationResponse extends BaseResponse { this.userId = this.getResponseProperty('UserId'); this.providerId = this.getResponseProperty('ProviderId'); this.providerName = this.getResponseProperty('ProviderName'); + this.familySponsorshipFriendlyName = this.getResponseProperty('FamilySponsorshipFriendlyName'); + this.familySponsorshipAvailable = this.getResponseProperty('FamilySponsorshipAvailable'); + this.planProductType = this.getResponseProperty('PlanProductType'); this.keyConnectorEnabled = this.getResponseProperty('KeyConnectorEnabled') ?? false; this.keyConnectorUrl = this.getResponseProperty('KeyConnectorUrl'); } diff --git a/common/src/models/response/subscriptionResponse.ts b/common/src/models/response/subscriptionResponse.ts index df2581ceab..616d5a8b98 100644 --- a/common/src/models/response/subscriptionResponse.ts +++ b/common/src/models/response/subscriptionResponse.ts @@ -59,6 +59,7 @@ export class BillingSubscriptionItemResponse extends BaseResponse { amount: number; quantity: number; interval: string; + sponsoredSubscriptionItem: boolean; constructor(response: any) { super(response); @@ -66,6 +67,7 @@ export class BillingSubscriptionItemResponse extends BaseResponse { this.amount = this.getResponseProperty('Amount'); this.quantity = this.getResponseProperty('Quantity'); this.interval = this.getResponseProperty('Interval'); + this.sponsoredSubscriptionItem = this.getResponseProperty('SponsoredSubscriptionItem'); } } diff --git a/common/src/services/api.service.ts b/common/src/services/api.service.ts index 5e3ff4bcf2..97145297db 100644 --- a/common/src/services/api.service.ts +++ b/common/src/services/api.service.ts @@ -33,6 +33,8 @@ import { ImportDirectoryRequest } from '../models/request/importDirectoryRequest import { ImportOrganizationCiphersRequest } from '../models/request/importOrganizationCiphersRequest'; import { KdfRequest } from '../models/request/kdfRequest'; import { KeysRequest } from '../models/request/keysRequest'; +import { OrganizationSponsorshipCreateRequest } from '../models/request/organization/organizationSponsorshipCreateRequest'; +import { OrganizationSponsorshipRedeemRequest } from '../models/request/organization/organizationSponsorshipRedeemRequest'; import { OrganizationSsoRequest } from '../models/request/organization/organizationSsoRequest'; import { OrganizationCreateRequest } from '../models/request/organizationCreateRequest'; import { OrganizationImportRequest } from '../models/request/organizationImportRequest'; @@ -172,6 +174,8 @@ import { KeyConnectorUserKeyRequest } from '../models/request/keyConnectorUserKe import { KeyConnectorUserKeyResponse } from '../models/response/keyConnectorUserKeyResponse'; import { SendAccessView } from '../models/view/sendAccessView'; + + export class ApiService implements ApiServiceAbstraction { protected apiKeyRefresh: (clientId: string, clientSecret: string) => Promise; private device: DeviceType; @@ -1558,6 +1562,35 @@ export class ApiService implements ApiServiceAbstraction { } } + async postCreateSponsorship(sponsoredOrgId: string, request: OrganizationSponsorshipCreateRequest): Promise { + return await this.send('POST', + '/organization/sponsorship/' + sponsoredOrgId + '/families-for-enterprise', + request, true, false); + } + + async deleteRevokeSponsorship(sponsoringOrganizationId: string): Promise { + return await this.send('DELETE', + '/organization/sponsorship/' + sponsoringOrganizationId, + null, true, false); + } + + async deleteRemoveSponsorship(sponsoringOrgId: string): Promise { + return await this.send('DELETE', + '/organization/sponsorship/sponsored/' + sponsoringOrgId, + null, true, false); + } + async postRedeemSponsorship(sponsorshipToken: string, request: OrganizationSponsorshipRedeemRequest): Promise { + return await this.send('POST', '/organization/sponsorship/redeem?sponsorshipToken=' + encodeURIComponent(sponsorshipToken), + request, true, false); + } + + async postResendSponsorshipOffer(sponsoringOrgId: string): Promise { + return await this.send('POST', + '/organization/sponsorship/' + sponsoringOrgId + '/families-for-enterprise/resend', + null, true, false); + } + + protected async doAuthRefresh(): Promise { const refreshToken = await this.tokenService.getRefreshToken(); if (refreshToken != null && refreshToken !== '') { diff --git a/common/src/services/user.service.ts b/common/src/services/user.service.ts index 658179f94b..a572691afd 100644 --- a/common/src/services/user.service.ts +++ b/common/src/services/user.service.ts @@ -156,6 +156,11 @@ export class UserService implements UserServiceAbstraction { return false; } + async canManageSponsorships(): Promise { + const orgs = await this.getAllOrganizations(); + return orgs.some(o => o.familySponsorshipAvailable || o.familySponsorshipFriendlyName !== null); + } + async getOrganization(id: string): Promise { const userId = await this.getUserId(); const organizations = await this.storageService.get<{ [id: string]: OrganizationData; }>(