1 import { c } from 'ttag';
6 ADDRESS_PERMISSION_TYPE,
12 } from '@proton/shared/lib/constants';
13 import { hasBit } from '@proton/shared/lib/helpers/bitset';
16 CachedOrganizationKey,
20 } from '@proton/shared/lib/interfaces';
21 import { AddressConfirmationState } from '@proton/shared/lib/interfaces';
22 import { getCanGenerateMemberAddressKeys } from '@proton/shared/lib/keys/memberKeys';
23 import { getIsNonDefault } from '@proton/shared/lib/mail/addresses';
25 const { TYPE_ORIGINAL, TYPE_CUSTOM_DOMAIN, TYPE_PREMIUM } = ADDRESS_TYPE;
27 export const getStatus = (address: Address, i: number) => {
28 const { Type, Status, Receive, DomainID, HasKeys, Flags } = address;
30 const isActive = Status === ADDRESS_STATUS.STATUS_ENABLED && Receive === ADDRESS_RECEIVE.RECEIVE_YES;
31 const isDisabled = Status === ADDRESS_STATUS.STATUS_DISABLED;
32 const isExternal = Type === ADDRESS_TYPE.TYPE_EXTERNAL;
33 const isOrphan = DomainID === null && !isExternal;
34 const isMissingKeys = !HasKeys;
35 const isNotEncrypted = hasBit(Flags, ADDRESS_FLAGS.FLAG_DISABLE_E2EE);
36 const isSignatureNotExpected = hasBit(Flags, ADDRESS_FLAGS.FLAG_DISABLE_EXPECTED_SIGNED);
46 isSignatureNotExpected,
50 export const getPermissions = ({
61 addresses: PartialMemberAddress[];
63 organizationKey?: CachedOrganizationKey;
65 const { isAdmin, canPay } = user;
66 const { ID, Status, Type, Priority, ConfirmationState } = address;
68 const isSpecialAddress = Type === TYPE_ORIGINAL || Type === TYPE_PREMIUM;
70 const isSelf = !member || !!member.Self;
71 const isDefault = addressIndex === 0;
72 const isEnabled = Status === ADDRESS_STATUS.STATUS_ENABLED;
73 const isExternal = Type === ADDRESS_TYPE.TYPE_EXTERNAL;
75 const canMakeDefault = !isDefault && !getIsNonDefault(address);
77 const canGenerateMemberAddressKeys = getCanGenerateMemberAddressKeys({ user, member, organizationKey, address });
79 * Even though the user in question regarding the permissions here might be
80 * the currently logged in user itself (isSelf), it's possible that they don't
81 * have the necessary permission to generate their own missing keys. This is
82 * the case if the currently logged in user is a member of an org of which they
83 * are not an admin of.
85 const canGenerateSelfAddressKeys = isSelf && user.Private === MEMBER_PRIVATE.UNREADABLE && !address.HasKeys;
86 const canGenerate = canGenerateMemberAddressKeys || canGenerateSelfAddressKeys;
88 let canDisable = isEnabled && isAdmin && !isSpecialAddress && !isExternal;
90 const isManagedUser = member?.Type === MEMBER_TYPE.MANAGED;
92 const hasOtherEnabledAddress = addresses.some(
93 (otherAddress) => otherAddress.ID !== ID && otherAddress.Status === ADDRESS_STATUS.STATUS_ENABLED
95 // Accounts for: 'Cannot disable your only enabled address. Please add another address first'
96 if (!hasOtherEnabledAddress && isSelf) {
99 // Accounts for: 'Cannot disable your default address. Please make another address default first'
100 if (hasOtherEnabledAddress && Priority === 1) {
105 const canEditInternalAddress = Type !== ADDRESS_TYPE.TYPE_EXTERNAL && isSelf;
106 const canEditExternalAddress =
107 Type === ADDRESS_TYPE.TYPE_EXTERNAL &&
108 ConfirmationState !== AddressConfirmationState.CONFIRMATION_CONFIRMED &&
111 // Takes into account disabling permissions since it does that automatically. canPay to simulate the "payments" scope for delete route.
112 const adminCanDeleteCustom = ((isEnabled && canDisable) || !isEnabled) && Type === TYPE_CUSTOM_DOMAIN && canPay;
117 canEditInternalAddress,
118 canEditExternalAddress,
120 canEnable: Status === ADDRESS_STATUS.STATUS_DISABLED && isAdmin && !isSpecialAddress,
121 canDeleteAddress: adminCanDeleteCustom,
122 canDeleteAddressOncePerYear: !adminCanDeleteCustom && isAdmin && !isSpecialAddress && !isExternal && !isDefault,
127 export type AddressPermissions = ReturnType<typeof getPermissions>;
128 export type AddressStatuses = ReturnType<typeof getStatus>;
130 export interface AddressWithMemberID extends PartialMemberAddress {
135 export interface MembersMap {
136 [MemberID: string]: Member;
139 export interface SwitchAddressPermissionMultiResponses {
140 Responses: SwitchAddressPermissionResponse[];
143 export interface SwitchAddressPermissionResponse {
150 export interface PermissionOption {
152 value: ADDRESS_PERMISSIONS;
156 export const canReceive = (permissions: number): boolean => {
158 hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ALL) ||
159 hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ORG)
163 export const canSend = (permissions: number): boolean => {
165 hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ALL) ||
166 hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ORG)
170 export const hasIncompleteSetup = (permissions: number): boolean => {
171 return !canReceive(permissions) && !canSend(permissions);
174 export const noPermissionMap = (): PermissionOption[] => {
177 text: c('Option').t`No permission`,
178 value: ADDRESS_PERMISSIONS.NO_PERMISSION,
179 testid: 'permission-map:none',
184 export const setupIncompletePermissionMap = (): PermissionOption[] => {
187 text: c('Option').t`Setup incomplete`,
188 value: ADDRESS_PERMISSIONS.NO_PERMISSION,
189 testid: 'permission-map:setup-incomplete',
194 export const permissionsReceiveMap = (): PermissionOption[] => {
197 text: c('Option').t`Receive from all`,
198 value: ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ALL,
199 testid: 'permission-dropdown:receive-all',
202 text: c('Option').t`Organization only`,
203 value: ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ORG,
204 testid: 'permission-dropdown:receive-org',
209 export const permissionsSendMap = (): PermissionOption[] => {
212 text: c('Option').t`Send to all`,
213 value: ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ALL,
214 testid: 'permission-dropdown:send-all',
217 text: c('Option').t`Organization only`,
218 value: ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ORG,
219 testid: 'permission-dropdown:send-org',
224 export const getReceivePermission = (permissions: number) => {
225 if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ALL)) {
226 return ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ALL;
228 if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ORG)) {
229 return ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ORG;
231 return ADDRESS_PERMISSIONS.NO_PERMISSION;
234 export const getSendPermission = (permissions: number) => {
235 if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ALL)) {
236 return ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ALL;
238 if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ORG)) {
239 return ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ORG;
241 return ADDRESS_PERMISSIONS.NO_PERMISSION;
244 export const permissionsMap = (addressPermissions: number | undefined, type: ADDRESS_PERMISSION_TYPE) => {
245 if (addressPermissions && !hasIncompleteSetup(addressPermissions)) {
247 type === ADDRESS_PERMISSION_TYPE.RECEIVE &&
248 getReceivePermission(addressPermissions) !== ADDRESS_PERMISSIONS.NO_PERMISSION
250 return permissionsReceiveMap();
253 type === ADDRESS_PERMISSION_TYPE.SEND &&
254 getSendPermission(addressPermissions) !== ADDRESS_PERMISSIONS.NO_PERMISSION
256 return permissionsSendMap();
259 if (!addressPermissions || hasIncompleteSetup(addressPermissions)) {
260 return setupIncompletePermissionMap();
262 return noPermissionMap();
265 export const getPermission = (permissions: number, type: ADDRESS_PERMISSION_TYPE): string => {
266 if (type === ADDRESS_PERMISSION_TYPE.RECEIVE) {
267 if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ALL)) {
268 return c('Permission').t`Receive from all`;
270 if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ORG)) {
271 return c('Permission').t`Organization only`;
273 if (hasIncompleteSetup(permissions)) {
274 return c('Permission').t`Setup incomplete`;
277 if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ALL)) {
278 return c('Permission').t`Send to all`;
280 if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ORG)) {
281 return c('Permission').t`Organization only`;
283 if (hasIncompleteSetup(permissions)) {
284 return c('Permission').t`Setup incomplete`;
288 return c('Permission').t`No permission`;