Skip to content

Commit

Permalink
feat(key-management-service): create credential step 3 (#13157)
Browse files Browse the repository at this point in the history
ref: 14180

Signed-off-by: Vincent BONMARCHAND <[email protected]>
  • Loading branch information
vovh authored and Romain Jamet committed Sep 23, 2024
1 parent c0ddc70 commit faa1db3
Show file tree
Hide file tree
Showing 48 changed files with 273 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,16 @@
"key_management_service_credential_create_identities_user_tile_group_label": "Groupe",
"key_management_service_credential_create_identities_user_tile_identity_label": "Identité",
"key_management_service_credential_create_success": "La création de votre certificat est en cours. Vous pourrez télécharger la clé publique depuis la page “Certificat d’accès” de votre KMS dans quelques minutes.",
"key_management_service_credential_create_error": "La création de votre certificat a rencontré des erreurs."
"key_management_service_credential_create_error": "La création de votre certificat a rencontré des erreurs.",
"key_management_service_credential_create_confirmation_details_title": "Certificat d’accès",
"key_management_service_credential_create_confirmation_details_id_label": "ID du certificat",
"key_management_service_credential_create_confirmation_details_display-name_label": "Nom d’affichage",
"key_management_service_credential_create_confirmation_details_description_label": "Description",
"key_management_service_credential_create_confirmation_details_validity_label": "Validité",
"key_management_service_credential_create_confirmation_private-key_title": "Clé privée",
"key_management_service_credential_create_confirmation_private-key_warn": "Vous n’aurez plus accès à cette clé privée lorsque vous quitterez l’assistant de création. Veillez à la télécharger et à la stocker de manière sécurisée.",
"key_management_service_credential_create_confirmation_private-key_download_label": "Télécharger la clé privée",
"key_management_service_credential_create_confirmation_private-key_checkbox_label": "Je confirme avoir téléchargé la clé privée",
"key_management_service_credential_create_confirmation_button_done_label": "Terminer",
"key_management_service_credential_create_confirmation_details_validity_suffix": "{{days}} jours"
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useTranslation } from 'react-i18next';
import { IdentityGroup } from '@/types/identity.type';
import { useIdentityData } from '@/hooks/credential/useIdentityData';
import { useIdentityGroupList } from '@/data/hooks/useIdentity';
import IdentitiesGroupList from '@/components/credential/create/identities/list/IdentitiesGroupList.component';
import IdentitiesGroupList from '@/pages/credential/create/identities/list/IdentitiesGroupList.component';

type IdentityGroupListModalProps = {
closeModal: () => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useTranslation } from 'react-i18next';
import { IdentityOauthClient } from '@/types/identity.type';
import { useIdentityData } from '@/hooks/credential/useIdentityData';
import { useIdentityServiceAccountList } from '@/data/hooks/useIdentity';
import IdentitiesServiceAccountList from '@/components/credential/create/identities/list/IdentitiesServiceAccountList.component';
import IdentitiesServiceAccountList from '@/pages/credential/create/identities/list/IdentitiesServiceAccountList.component';

type IdentityServiceAccountListModalProps = {
closeModal: () => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useTranslation } from 'react-i18next';
import { IdentityUser } from '@/types/identity.type';
import { useIdentityData } from '@/hooks/credential/useIdentityData';
import { useIdentityUserList } from '@/data/hooks/useIdentity';
import IdentitiesUserList from '@/components/credential/create/identities/list/IdentitiesUserList.component';
import IdentitiesUserList from '@/pages/credential/create/identities/list/IdentitiesUserList.component';

type IdentityUserListModalProps = {
closeModal: () => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ export const createOkmsCredential = async ({
okmsId: string;
data: OkmsCredentialCreation;
}) => {
return apiClient.v2.post(`okms/resource/${okmsId}/credential`, data);
return apiClient.v2.post<OkmsCredential>(
`okms/resource/${okmsId}/credential`,
data,
);
};

export const createOkmsCredentialQueryKey = ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import { ApiError } from '@ovh-ux/manager-core-api';
import { useTranslation } from 'react-i18next';
import { useNotifications } from '@ovh-ux/manager-react-components';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { OkmsCredentialCreation } from '@/types/okmsCredential.type';
import {
OkmsCredential,
OkmsCredentialCreation,
} from '@/types/okmsCredential.type';
import { createOkmsCredential } from '../api/okmsCredential';
import { getOkmsCredentialsQueryKey } from './useOkmsCredential';

export type CreateOkmsCredentialParams = {
okmsId: string;
onSuccess: () => void;
onSuccess: (credential: OkmsCredential) => void;
onError: () => void;
};

Expand All @@ -23,15 +26,16 @@ export const useCreateOkmsCredential = ({
const { t } = useTranslation('key-management-service/credential');

const { mutate: createKmsCredential, isPending } = useMutation({
mutationFn: (data: OkmsCredentialCreation) => {
return createOkmsCredential({ okmsId, data });
mutationFn: async (data: OkmsCredentialCreation) => {
const okmsCredential = await createOkmsCredential({ okmsId, data });
return okmsCredential.data;
},
onSuccess: async () => {
onSuccess: async (credential: OkmsCredential) => {
await queryClient.invalidateQueries({
queryKey: getOkmsCredentialsQueryKey(okmsId),
});
addSuccess(t('key_management_service_credential_create_success'), true);
onSuccess?.();
onSuccess?.(credential);
},
onError: (result: ApiError) => {
addError(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Suspense, useState } from 'react';
import React, { Suspense, useEffect, useState } from 'react';
import {
BaseLayout,
ErrorBanner,
Expand All @@ -14,8 +14,10 @@ import { useOKMSById } from '@/data/hooks/useOKMS';
import Loading from '@/components/Loading/Loading';
import { IdentityDataProvider } from '@/hooks/credential/useIdentityData';
import { useCreateOkmsCredential } from '@/data/hooks/useCreateOkmsCredential';
import CreateGeneralInformations from '@/components/credential/create/CreateGeneralInformations.component';
import CreateAddIdentities from '@/components/credential/create/CreateAddIdentities.component';
import CreateGeneralInformations from '@/pages/credential/create/CreateGeneralInformations.component';
import CreateAddIdentities from '@/pages/credential/create/CreateAddIdentities.component';
import CreateCredentialConfirmation from '@/pages/credential/create/confirmation/CreateCredentialConfirmation.component';
import { OkmsCredential } from '@/types/okmsCredential.type';

const CreateCredential = () => {
const navigate = useNavigate();
Expand All @@ -28,10 +30,11 @@ const CreateCredential = () => {
const [description, setDescription] = useState<string | null>();
const [csr, setCsr] = useState<string | null>(null);
const [identityURNs, setIdentityURNs] = useState<string[]>([]);
const [okmsCredential, setOkmsCredential] = useState<OkmsCredential>();
const { createKmsCredential } = useCreateOkmsCredential({
okmsId,
onSuccess: () => {
navigate(`/${okmsId}/${ROUTES_URLS.credentials}`);
onSuccess: (credential) => {
setOkmsCredential(credential);
},
onError: () => {},
});
Expand All @@ -54,6 +57,16 @@ const CreateCredential = () => {
},
];

useEffect(() => {
if (okmsCredential) {
if (!okmsCredential.fromCSR) {
setStep(3);
} else {
navigate(`/${okmsId}/${ROUTES_URLS.credentials}`);
}
}
}, [okmsCredential]);

if (isLoading) return <Loading />;

if (error)
Expand Down Expand Up @@ -104,12 +117,15 @@ const CreateCredential = () => {
name,
identityURNs,
description,
csr,
validity,
...(csr ? { csr } : {}),
});
}}
></CreateAddIdentities>
)}
{step === 3 && (
<CreateCredentialConfirmation okmsCredential={okmsCredential} />
)}
</div>
<Outlet />
</IdentityDataProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Dispatch, SetStateAction, useState } from 'react';
import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { Subtitle } from '@ovh-ux/manager-react-components';
import { useTranslation } from 'react-i18next';
import { OsdsButton } from '@ovhcloud/ods-components/react';
Expand Down Expand Up @@ -48,6 +48,12 @@ const CreateGeneralInformations = ({
const credentialCreationMethodError = validateCredentialCreationMethod(csr);
const [isCustomCsr, setIsCustomCsr] = useState<boolean>(false);

useEffect(() => {
if (!isCustomCsr) {
setCsr(null);
}
}, [isCustomCsr]);

return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-6">
<div className="flex flex-col gap-7 md:gap-9">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useState } from 'react';
import { OsdsButton } from '@ovhcloud/ods-components/react';
import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
import { useTranslation } from 'react-i18next';
import { useNavigate, useParams } from 'react-router-dom';
import CreateCredentialConfirmationDetails from './CreateCredentialConfirmationDetails.component';
import CreateCredentialConfirmationPrivateKey from './CreateCredentialConfirmationPrivateKey.component';
import { OkmsCredential } from '@/types/okmsCredential.type';
import { ROUTES_URLS } from '@/routes/routes.constants';

type CreateCredentialConfirmationProps = {
okmsCredential: OkmsCredential;
};

const CreateCredentialConfirmation = ({
okmsCredential,
}: CreateCredentialConfirmationProps) => {
const { t } = useTranslation('key-management-service/credential');
const [isKeyDownloaded, setIsKeyDownloaded] = useState(false);
const { okmsId } = useParams();
const navigate = useNavigate();

return (
<div className="grid grid-cols-1 gap-4 md:gap-6 max-w-screen-lg">
<CreateCredentialConfirmationDetails okmsCredential={okmsCredential} />
<CreateCredentialConfirmationPrivateKey
privateKey={okmsCredential.privateKeyPEM}
credentialId={okmsCredential.id}
isKeyDownloaded={isKeyDownloaded}
setIsKeyDownloaded={setIsKeyDownloaded}
/>
<span>
<OsdsButton
color={ODS_THEME_COLOR_INTENT.primary}
disabled={!isKeyDownloaded || undefined}
onClick={() => navigate(`/${okmsId}/${ROUTES_URLS.credentials}`)}
inline
>
{t(
'key_management_service_credential_create_confirmation_button_done_label',
)}
</OsdsButton>
</span>
</div>
);
};

export default CreateCredentialConfirmation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Subtitle } from '@ovh-ux/manager-react-components';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { OsdsTile } from '@ovhcloud/ods-components/react';
import CreateCredentialConfirmationDetailsText from './CreateCredentialConfirmationDetailsText.component';
import { OkmsCredential } from '@/types/okmsCredential.type';
import { getDaysFromDate } from '@/utils/credential/validityDateUtils';

type CreateCredentialConfirmationDetailsProps = {
okmsCredential: OkmsCredential;
};

const CreateCredentialConfirmationDetails = ({
okmsCredential,
}: CreateCredentialConfirmationDetailsProps) => {
const { t } = useTranslation('key-management-service/credential');

return (
<OsdsTile>
<div className="flex flex-col w-full gap-7 md:gap-9">
<Subtitle>
{t(
'key_management_service_credential_create_confirmation_details_title',
)}
</Subtitle>
<CreateCredentialConfirmationDetailsText
label={t(
'key_management_service_credential_create_confirmation_details_id_label',
)}
value={okmsCredential.id}
/>
<CreateCredentialConfirmationDetailsText
label={t(
'key_management_service_credential_create_confirmation_details_display-name_label',
)}
value={okmsCredential.name}
/>
<CreateCredentialConfirmationDetailsText
label={t(
'key_management_service_credential_create_confirmation_details_description_label',
)}
value={okmsCredential.description}
/>
<CreateCredentialConfirmationDetailsText
label={t(
'key_management_service_credential_create_confirmation_details_validity_label',
)}
value={t(
'key_management_service_credential_create_confirmation_details_validity_suffix',
{ days: getDaysFromDate(new Date(okmsCredential.expiredAt)) },
)}
/>
</div>
</OsdsTile>
);
};

export default CreateCredentialConfirmationDetails;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Description } from '@ovh-ux/manager-react-components';
import React from 'react';

type CreateCredentialConfirmationDetailsTextProps = {
label: string;
value: string;
};

const CreateCredentialConfirmationDetailsText = ({
label,
value,
}: CreateCredentialConfirmationDetailsTextProps) => {
return (
<div className="grid grid-cols-2">
<Description>{`${label}:`}</Description>
<Description>{value}</Description>
</div>
);
};
export default CreateCredentialConfirmationDetailsText;
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Subtitle } from '@ovh-ux/manager-react-components';
import {
ODS_BUTTON_SIZE,
ODS_BUTTON_VARIANT,
ODS_ICON_NAME,
} from '@ovhcloud/ods-components';
import {
OsdsButton,
OsdsCheckbox,
OsdsCheckboxButton,
OsdsMessage,
OsdsText,
OsdsTile,
} from '@ovhcloud/ods-components/react';
import React, { Dispatch, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next';
import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';

type CreateCredentialConfirmationPrivateKeyProps = {
privateKey: string;
credentialId: string;
isKeyDownloaded: boolean;
setIsKeyDownloaded: Dispatch<SetStateAction<boolean>>;
};

const CreateCredentialConfirmationPrivateKey = ({
privateKey,
credentialId,
isKeyDownloaded,
setIsKeyDownloaded,
}: CreateCredentialConfirmationPrivateKeyProps) => {
const { t } = useTranslation('key-management-service/credential');
return (
<OsdsTile>
<div className="flex flex-col w-full gap-7 md:gap-9">
<Subtitle>
{t(
'key_management_service_credential_create_confirmation_private-key_title',
)}
</Subtitle>
<OsdsMessage
icon={ODS_ICON_NAME.WARNING}
color={ODS_THEME_COLOR_INTENT.warning}
>
{t(
'key_management_service_credential_create_confirmation_private-key_warn',
)}
</OsdsMessage>
<span>
<OsdsButton
variant={ODS_BUTTON_VARIANT.stroked}
color={ODS_THEME_COLOR_INTENT.primary}
href={`data:text/plain;charset=utf-8,${encodeURIComponent(
privateKey.replace(/\n/g, '\r\n'),
)}`}
download={`${credentialId}_privatekey.pem`}
inline
size={ODS_BUTTON_SIZE.sm}
>
{t(
'key_management_service_credential_create_confirmation_private-key_download_label',
)}
</OsdsButton>
</span>
<OsdsCheckbox
checked={isKeyDownloaded}
onOdsCheckedChange={() => setIsKeyDownloaded(!isKeyDownloaded)}
>
<OsdsCheckboxButton color={ODS_THEME_COLOR_INTENT.primary}>
<span slot="end">
<OsdsText color={ODS_THEME_COLOR_INTENT.text}>
{t(
'key_management_service_credential_create_confirmation_private-key_checkbox_label',
)}
</OsdsText>
</span>
</OsdsCheckboxButton>
</OsdsCheckbox>
</div>
</OsdsTile>
);
};

export default CreateCredentialConfirmationPrivateKey;
Loading

0 comments on commit faa1db3

Please sign in to comment.