Flavien modal two
[ProtonMail-WebClient.git] / packages / components / containers / organization / sso / scim / SCIMSettingsSection.tsx
blob1f82786312443338413de1ff2f084b9c3edbeba1
1 import type { ReactNode } from 'react';
2 import { useState } from 'react';
4 import type { Action, ThunkDispatch } from '@reduxjs/toolkit';
5 import { c } from 'ttag';
7 import type { SamlState } from '@proton/account';
8 import { disableSCIMAction, setupSCIMAction } from '@proton/account/samlSSO/actions';
9 import { Button } from '@proton/atoms';
10 import Icon from '@proton/components/components/icon/Icon';
11 import Info from '@proton/components/components/link/Info';
12 import useModalState from '@proton/components/components/modalTwo/useModalState';
13 import useLoading from '@proton/hooks/useLoading';
14 import { baseUseDispatch } from '@proton/react-redux-store';
15 import type { ProtonThunkArguments } from '@proton/redux-shared-store-types';
16 import { VPN_APP_NAME } from '@proton/shared/lib/constants';
17 import type { Domain } from '@proton/shared/lib/interfaces';
18 import { DOMAIN_STATE } from '@proton/shared/lib/interfaces';
20 import getBoldFormattedText from '../../../../helpers/getBoldFormattedText';
21 import { useApi, useNotifications } from '../../../../hooks';
22 import {
23     SettingsLayout,
24     SettingsLayoutLeft,
25     SettingsLayoutRight,
26     SettingsParagraph,
27     SettingsSectionWide,
28 } from '../../../account';
29 import SubSettingsSection from '../../../layout/SubSettingsSection';
30 import ReadonlyFieldWithCopy from '../ReadonlyFieldWithCopy';
31 import DisableSCIMModal from './DisableSCIMModal';
32 import RegenerateSCIMConfirmModal from './RegenerateSCIMConfirmModal';
33 import type { SCIMConfiguration } from './SetupSCIMModal';
34 import SetupSCIMModal from './SetupSCIMModal';
36 interface Props {
37     domain?: Domain;
38     onConfigureSaml: () => void;
39     onShowVerifyDomain: () => void;
40     hasSsoConfig: boolean;
41     scimInfo: NonNullable<SamlState['sso']['value']>['scimInfo'];
44 const PreReq = ({ data, action }: { data: ReactNode; action: ReactNode }) => {
45     return (
46         <div className="rounded border bg-weak p-4 flex justify-space-between gap-2 items-center lg:flex-nowrap">
47             <div className="flex gap-2 items-start flex-nowrap">
48                 <Icon name="info-circle" className="shrink-0" />
49                 <p className="m-0">{data}</p>
50             </div>
51             {action}
52         </div>
53     );
56 const SCIMSettingsSection = ({ domain, onConfigureSaml, onShowVerifyDomain, hasSsoConfig, scimInfo }: Props) => {
57     const api = useApi();
58     const dispatch = baseUseDispatch<ThunkDispatch<SamlState, ProtonThunkArguments, Action>>();
59     const { createNotification } = useNotifications();
61     const [setupSCIMModalProps, setSetupSCIMModalOpen, renderSetupSCIMModal] = useModalState();
62     const [regenerateSCIMModalProps, setRegenerateSCIMModalOpen, renderRegenerateSCIMModal] = useModalState();
63     const [disableSCIMModalProps, setDisableSCIMModalOpen, renderDisableSCIMModal] = useModalState();
64     const [localSCIMConfiguration, setLocalSCIMConfiguration] = useState<SCIMConfiguration>();
65     const [loadingSCIM, withLoadingSCIM] = useLoading();
67     return (
68         <>
69             <SubSettingsSection
70                 id="scim-automatic-provisioning"
71                 title={c('scim: Title').t`SCIM automatic provisioning`}
72                 className="container-section-sticky-section"
73                 beta
74             >
75                 <SettingsSectionWide>
76                     <SettingsParagraph learnMoreUrl="">
77                         {c('scim: Info')
78                             .t`Simplifies user management across different services. Add, edit, and remove users in your identity provider, and those changes will automatically be applied to your ${VPN_APP_NAME} organization.`}
79                     </SettingsParagraph>
81                     {(() => {
82                         if (!hasSsoConfig) {
83                             return (
84                                 <PreReq
85                                     data={getBoldFormattedText(
86                                         c('scim: Info')
87                                             .t`**Prerequisite:** SAML authentication must be configured to enable SCIM provisioning.`
88                                     )}
89                                     action={
90                                         <Button
91                                             color="weak"
92                                             shape="solid"
93                                             size="small"
94                                             className="shrink-0"
95                                             onClick={onConfigureSaml}
96                                         >
97                                             {c('Action').t`Configure SAML`}
98                                         </Button>
99                                     }
100                                 />
101                             );
102                         }
104                         if (!scimInfo.state) {
105                             const domainName = domain?.DomainName || '';
106                             const isVerified = domain?.State !== DOMAIN_STATE.DOMAIN_STATE_DEFAULT;
107                             return (
108                                 <>
109                                     <SettingsLayout>
110                                         {!isVerified ? (
111                                             <PreReq
112                                                 data={getBoldFormattedText(
113                                                     c('scim: Info')
114                                                         .t`**Prerequisite:** to enable SCIM provisioning, you must verify ownership of the domain **${domainName}**. This verification can take up to one hour.`
115                                                 )}
116                                                 action={
117                                                     <Button
118                                                         color="weak"
119                                                         shape="solid"
120                                                         size="small"
121                                                         className="shrink-0"
122                                                         onClick={onShowVerifyDomain}
123                                                     >
124                                                         {c('Action').t`Verify domain`}
125                                                     </Button>
126                                                 }
127                                             />
128                                         ) : (
129                                             <Button
130                                                 color="norm"
131                                                 shape="outline"
132                                                 className="shrink-0 grow-0"
133                                                 onClick={() => {
134                                                     const run = async () => {
135                                                         const result = await dispatch(
136                                                             setupSCIMAction({
137                                                                 type: 'setup',
138                                                                 api,
139                                                             })
140                                                         );
141                                                         setLocalSCIMConfiguration({ ...result, type: 'setup' });
142                                                         setSetupSCIMModalOpen(true);
143                                                         createNotification({ text: c('Info').t`SCIM token active` });
144                                                     };
145                                                     void withLoadingSCIM(run());
146                                                 }}
147                                                 loading={loadingSCIM}
148                                             >
149                                                 {c('scim: Action').t`Configure SCIM`}
150                                             </Button>
151                                         )}
152                                     </SettingsLayout>
153                                 </>
154                             );
155                         }
157                         return (
158                             <>
159                                 <SettingsLayout>
160                                     <SettingsLayoutLeft>
161                                         <label htmlFor="scimURL" className="text-semibold flex items-center gap-2">
162                                             <span>{c('scim: Label').t`SCIM base URL`}</span>
163                                             <Info
164                                                 title={c('scim: Tooltip')
165                                                     .t`URL of the SCIM connector to which your IdP Provisioning Agent forwards SCIM data`}
166                                             />
167                                         </label>
168                                     </SettingsLayoutLeft>
169                                     <SettingsLayoutRight className="w-full">
170                                         <ReadonlyFieldWithCopy id="scimURL" value={scimInfo.baseUrl} />
171                                     </SettingsLayoutRight>
172                                 </SettingsLayout>
173                                 <SettingsLayout>
174                                     <SettingsLayoutLeft>
175                                         <label htmlFor="scimToken" className="text-semibold flex items-center gap-2">
176                                             <span>{c('scim: Label').t`SCIM token`}</span>
177                                             <Info
178                                                 title={c('scim: Tooltip')
179                                                     .t`API key of the SCIM connector to which your IdP Provisioning Agent forwards SCIM data`}
180                                             />
181                                         </label>
182                                     </SettingsLayoutLeft>
183                                     <SettingsLayoutRight className="w-full">
184                                         <div className="flex items-center gap-2 mb-4 mt-2">
185                                             <Icon name="eye-slash" className="shrink-0" />
186                                             <p className="m-0">{c('scim: Info')
187                                                 .t`For security reasons, the token is hidden`}</p>
188                                         </div>
190                                         <div className="border rounded p-4 flex items-start lg:flex-nowrap gap-2">
191                                             {c('scim: Info')
192                                                 .t`If you've lost or forgotten the SCIM token, you can generate a new one, but be aware that your identity provider settings will need to be updated.`}
193                                             <Button
194                                                 color="weak"
195                                                 shape="outline"
196                                                 size="small"
197                                                 className="shrink-0 grow-0"
198                                                 onClick={() => setRegenerateSCIMModalOpen(true)}
199                                                 loading={loadingSCIM}
200                                             >
201                                                 <Icon name="arrow-rotate-right" className="shrink-0 mr-1" />
202                                                 {c('scim: Action').t`Generate new token`}
203                                             </Button>
204                                         </div>
205                                     </SettingsLayoutRight>
206                                 </SettingsLayout>
208                                 <Button color="danger" shape="outline" onClick={() => setDisableSCIMModalOpen(true)}>
209                                     {c('scim: Action').t`Disable SCIM integration`}
210                                 </Button>
211                             </>
212                         );
213                     })()}
214                 </SettingsSectionWide>
215             </SubSettingsSection>
217             {renderSetupSCIMModal && localSCIMConfiguration && (
218                 <SetupSCIMModal {...setupSCIMModalProps} {...localSCIMConfiguration} />
219             )}
220             {renderRegenerateSCIMModal && (
221                 <RegenerateSCIMConfirmModal
222                     {...regenerateSCIMModalProps}
223                     onConfirm={async () => {
224                         const result = await dispatch(
225                             setupSCIMAction({
226                                 type: 'generated',
227                                 api,
228                             })
229                         );
230                         setLocalSCIMConfiguration({ ...result, type: 'generated' });
231                         setSetupSCIMModalOpen(true);
232                         regenerateSCIMModalProps.onClose();
233                         createNotification({ text: c('Info').t`SCIM token active` });
234                     }}
235                 />
236             )}
237             {renderDisableSCIMModal && (
238                 <DisableSCIMModal
239                     {...disableSCIMModalProps}
240                     onConfirm={async () => {
241                         await dispatch(
242                             disableSCIMAction({
243                                 api,
244                             })
245                         );
246                         setLocalSCIMConfiguration(undefined);
247                         disableSCIMModalProps.onClose();
248                         createNotification({ text: c('Info').t`SCIM integration disabled` });
249                     }}
250                 />
251             )}
252         </>
253     );
256 export default SCIMSettingsSection;