Skip to content

Commit a400ff7

Browse files
edlerdlorumic
authored andcommitted
feat(cert) ask for certificate password on creation #331
1 parent 4f8202f commit a400ff7

3 files changed

Lines changed: 105 additions & 5 deletions

File tree

src/pages/certificates/CertificateGenerate.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import BrowserImport from "pages/certificates/BrowserImport";
55
import { Navigate } from "react-router-dom";
66
import { useAuth } from "context/auth";
77
import Loader from "components/Loader";
8+
import PasswordModal from "pages/certificates/PasswordModal";
89

910
interface Certs {
1011
crt: string;
@@ -13,6 +14,7 @@ interface Certs {
1314

1415
const CertificateGenerate: FC = () => {
1516
const [isGenerating, setGenerating] = useState(false);
17+
const [isModalOpen, setModalOpen] = useState(false);
1618
const [certs, setCerts] = useState<Certs | null>(null);
1719
const { isAuthenticated, isAuthLoading } = useAuth();
1820

@@ -24,11 +26,20 @@ const CertificateGenerate: FC = () => {
2426
return <Navigate to="/ui" replace={true} />;
2527
}
2628

27-
const createCert = () => {
29+
const closeModal = () => {
30+
setModalOpen(false);
31+
};
32+
33+
const openModal = () => {
34+
setModalOpen(true);
35+
};
36+
37+
const createCert = (password: string) => {
38+
closeModal();
2839
setGenerating(true);
2940
// using timeout to avoid compute heavy generation in the main ui thread
3041
setTimeout(() => {
31-
const certs = generateCert();
42+
const certs = generateCert(password);
3243
setCerts(certs);
3344
setGenerating(false);
3445
}, 10);
@@ -76,8 +87,14 @@ const CertificateGenerate: FC = () => {
7687
</div>
7788
</Col>
7889
<Col size={3}>
90+
{isModalOpen && (
91+
<PasswordModal
92+
onClose={closeModal}
93+
onConfirm={createCert}
94+
/>
95+
)}
7996
<Button
80-
onClick={createCert}
97+
onClick={openModal}
8198
appearance="positive"
8299
disabled={isGenerating || certs !== null}
83100
hasIcon={isGenerating}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import React, { FC } from "react";
2+
import { Button, Input, Modal } from "@canonical/react-components";
3+
import { useFormik } from "formik";
4+
import * as Yup from "yup";
5+
6+
interface Props {
7+
onConfirm: (password: string) => void;
8+
onClose: () => void;
9+
}
10+
11+
const PasswordModal: FC<Props> = ({ onConfirm, onClose }) => {
12+
const PasswordSchema = Yup.object().shape({
13+
password: Yup.string(),
14+
passwordConfirm: Yup.string().oneOf(
15+
[Yup.ref("password"), null],
16+
"Passwords must match"
17+
),
18+
});
19+
20+
const formik = useFormik({
21+
initialValues: {
22+
password: "",
23+
passwordConfirm: "",
24+
},
25+
validationSchema: PasswordSchema,
26+
onSubmit: (values) => {
27+
onConfirm(values.password);
28+
},
29+
});
30+
31+
const handleSkip = () => {
32+
onConfirm("");
33+
};
34+
35+
return (
36+
<Modal
37+
close={onClose}
38+
title="Add a password"
39+
buttonRow={
40+
<>
41+
<Button className="u-no-margin--bottom" onClick={handleSkip}>
42+
Skip
43+
</Button>
44+
<Button
45+
appearance="positive"
46+
className="u-no-margin--bottom"
47+
onClick={() => formik.submitForm()}
48+
disabled={
49+
formik.values.password !== formik.values.passwordConfirm ||
50+
formik.values.password.length === 0
51+
}
52+
>
53+
Generate certificate
54+
</Button>
55+
</>
56+
}
57+
>
58+
<p>Protect your certificate by adding a password.</p>
59+
<Input
60+
id="password"
61+
type="password"
62+
label="Password"
63+
onBlur={formik.handleBlur}
64+
onChange={formik.handleChange}
65+
value={formik.values.password}
66+
error={formik.touched.password ? formik.errors.password : null}
67+
/>
68+
<Input
69+
id="passwordConfirm"
70+
type="password"
71+
label="Password confirmation"
72+
onBlur={formik.handleBlur}
73+
onChange={formik.handleChange}
74+
value={formik.values.passwordConfirm}
75+
error={
76+
formik.touched.passwordConfirm ? formik.errors.passwordConfirm : null
77+
}
78+
/>
79+
</Modal>
80+
);
81+
};
82+
83+
export default PasswordModal;

src/util/certificate.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const details = [
2525
},
2626
];
2727

28-
export const generateCert = () => {
28+
export const generateCert = (password: string) => {
2929
const validDays = 1000;
3030

3131
const keys = forge.pki.rsa.generateKeyPair(2048);
@@ -44,7 +44,7 @@ export const generateCert = () => {
4444

4545
const crt = forge.pki.certificateToPem(cert);
4646

47-
const asn1 = forge.pkcs12.toPkcs12Asn1(keys.privateKey, [cert], "", {
47+
const asn1 = forge.pkcs12.toPkcs12Asn1(keys.privateKey, [cert], password, {
4848
generateLocalKeyId: true,
4949
friendlyName: "LXD-UI",
5050
});

0 commit comments

Comments
 (0)