Skip to content

Commit

Permalink
Issue 5768 - CLI/UI - cert checks are too strict, and other issues
Browse files Browse the repository at this point in the history
Description:

The certificate type checks for CA/server break if there are no certificate
extensions set (use openssl in that case to gather the info instead).
dscontainter needed to be updated for new cert checks, and UI adding certs
improvements.

relates: #5768

Reviewed by: spichugi(Thanks!)
  • Loading branch information
mreynolds389 committed May 18, 2023
1 parent 77cb171 commit c0b8a21
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 172 deletions.
71 changes: 30 additions & 41 deletions src/cockpit/389-console/src/lib/security/certificateManagement.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export class CertificateManagement extends React.Component {
showExportModal: false,
certName: "",
certFile: "",
certText: "",
csrContent: "",
csrName: "",
csrAltNames: [],
Expand All @@ -74,9 +73,8 @@ export class CertificateManagement extends React.Component {
exportDERFormat: false,
exportFileName: "",
certRadioFile: false,
certRadioText: false,
certRadioSelect: true,
certRadioUpload: false,
certRadioSelect: false,
certRadioUpload: true,
availCertNames: [],
selectCertName: "",
isSelectCertOpen: false,
Expand All @@ -95,8 +93,8 @@ export class CertificateManagement extends React.Component {
};
this.handleTextOrDataChange = (value) => {
this.setState({
uploadValue: value
});
uploadValue: value.trim()
}, () => this.validateCertText());
};
this.handleFileReadStarted = () => {
this.setState({
Expand All @@ -110,8 +108,8 @@ export class CertificateManagement extends React.Component {
};
this.handleClear = () => {
this.setState({
certText: "",
uploadFile: "",
uploadValue: "",
uploadFileName: "",
uploadIsRejected: false,
});
};
Expand Down Expand Up @@ -212,15 +210,13 @@ export class CertificateManagement extends React.Component {
showAddModal: true,
certFile: "",
certName: "",
certText: "",
uploadFile: "",
uploadValue: "",
uploadIsRejected: false,
uploadIsLoading: false,
certRadioFile: false,
certRadioText: false,
certRadioSelect: true,
certRadioUpload: false,
certRadioSelect: false,
certRadioUpload: true,
isSelectCertOpen: false,
modalSpinning: false,
});
Expand All @@ -237,15 +233,13 @@ export class CertificateManagement extends React.Component {
showAddCAModal: true,
certFile: "",
certName: "",
certText: "",
uploadFile: "",
uploadValue: "",
uploadIsRejected: false,
uploadIsLoading: false,
certRadioFile: false,
certRadioText: false,
certRadioSelect: true,
certRadioUpload: false,
certRadioSelect: false,
certRadioUpload: true,
isSelectCertOpen: false,
modalSpinning: false,
});
Expand All @@ -260,20 +254,17 @@ export class CertificateManagement extends React.Component {
handleRadioChange(_, e) {
// Handle the add cert options
let certRadioFile = false;
let certRadioText = false;

let certRadioSelect = false;
let certRadioUpload = false;
if (e.target.id === "certRadioFile") {
certRadioFile = true;
} else if (e.target.id === "certRadioText") {
certRadioText = true;
} else if (e.target.id === "certRadioSelect") {
certRadioSelect = true;
} else if (e.target.id === "certRadioUpload") {
certRadioUpload = true;
}
this.setState({
certRadioText,
certRadioFile,
certRadioSelect,
certRadioUpload
Expand Down Expand Up @@ -468,10 +459,10 @@ export class CertificateManagement extends React.Component {
certType = "ca-certificate";
}

if ((this.state.certRadioText && this.state.certText !== "") || (this.state.certRadioUpload && this.state.uploadValue)) {
// Certificate was copied and pasted. Need to create a file for import
const certFile = this.props.certDir + "/" + this.state.certName + ".tmp";
const certText = this.state.certRadioUpload ? this.state.uploadValue : this.state.certText;
if (this.state.certRadioUpload && this.state.uploadValue) {
// Certificate was copied and pasted. Need to create a tmp file for import
const certFile = this.props.certDir + "/tmp-cert-" + Date.now() + ".tmp";
const certText = this.state.uploadValue;
const create_cert_cmd = [
'/bin/sh', '-c',
'/usr/bin/echo -e \'' + certText + '\' > ' + certFile
Expand Down Expand Up @@ -919,18 +910,17 @@ export class CertificateManagement extends React.Component {
});
}

validateCertText (id) {
if (id === "certText" && this.state.certText !== "") {
if (!this.state.certText.startsWith("-----BEGIN CERTIFICATE-----") ||
!this.state.certText.endsWith("-----END CERTIFICATE-----")) {
this.setState({
badCertText: true
});
} else {
this.setState({
badCertText: false
});
}
validateCertText () {
const value = this.state.uploadValue;
if (!value.startsWith("-----BEGIN CERTIFICATE-----") ||
!value.endsWith("-----END CERTIFICATE-----")) {
this.setState({
badCertText: true
});
} else {
this.setState({
badCertText: false
});
}
}

Expand All @@ -946,7 +936,7 @@ export class CertificateManagement extends React.Component {
this.setState({
[e.target.id]: value,
errObj: errObj
}, () => { this.validateCertText(e.target.id) });
});
}

handleCSRChange (e) {
Expand Down Expand Up @@ -1261,6 +1251,7 @@ export class CertificateManagement extends React.Component {
/>
<Button
variant="primary"
className="ds-margin-top-lg"
onClick={() => {
this.showAddCAModal();
}}
Expand All @@ -1280,6 +1271,7 @@ export class CertificateManagement extends React.Component {
/>
<Button
variant="primary"
className="ds-margin-top-lg"
onClick={() => {
this.showAddModal();
}}
Expand All @@ -1298,6 +1290,7 @@ export class CertificateManagement extends React.Component {
/>
<Button
variant="primary"
className="ds-margin-top-lg"
onClick={() => {
this.showAddCSRModal();
}}
Expand Down Expand Up @@ -1347,15 +1340,13 @@ export class CertificateManagement extends React.Component {
handleChange={this.handleChange}
saveHandler={this.addCert}
spinning={this.state.modalSpinning}
certText={this.state.certText}
certFile={this.state.certFile}
certName={this.state.certName}
certNames={this.state.availCertNames}
selectCertName={this.state.selectCertName}
isSelectCertOpen={this.state.isSelectCertOpen}
handleCertSelect={this.handleCertSelect}
certRadioFile={this.state.certRadioFile}
certRadioText={this.state.certRadioText}
certRadioSelect={this.state.certRadioSelect}
certRadioUpload={this.state.certRadioUpload}
handleRadioChange={this.handleRadioChange}
Expand All @@ -1378,15 +1369,13 @@ export class CertificateManagement extends React.Component {
handleChange={this.handleChange}
saveHandler={this.addCert}
spinning={this.state.modalSpinning}
certText={this.state.certText}
certFile={this.state.certFile}
certName={this.state.certName}
certNames={this.state.availCertNames}
selectCertName={this.state.selectCertName}
isSelectCertOpen={this.state.isSelectCertOpen}
handleCertSelect={this.handleCertSelect}
certRadioFile={this.state.certRadioFile}
certRadioText={this.state.certRadioText}
certRadioSelect={this.state.certRadioSelect}
certRadioUpload={this.state.certRadioUpload}
handleRadioChange={this.handleRadioChange}
Expand Down
118 changes: 59 additions & 59 deletions src/cockpit/389-console/src/lib/security/securityModals.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ export class SecurityAddCertModal extends React.Component {
certName,
certFile,
certText,
certRadioText,
certRadioFile,
certRadioSelect,
certRadioUpload,
Expand Down Expand Up @@ -179,17 +178,23 @@ export class SecurityAddCertModal extends React.Component {
</div>
}
>
<div>Certificate Text <OutlinedQuestionCircleIcon /></div>
<div>Upload PEM File, or Certificate Text <OutlinedQuestionCircleIcon /></div>
</Tooltip>
</div>;


let title = "Add Server Certificate";
let desc = "Add a Server Certificate to the security database.";
if (isCACert) {
title = "Add Certificate Authority";
desc = "Add a CA Certificate to the security database.";
}

let selectValidated = ValidatedOptions.default;
if (certRadioSelect && certNames.length === 0) {
selectValidated = ValidatedOptions.error;
}

return (
<Modal
variant={ModalVariant.medium}
Expand All @@ -207,9 +212,10 @@ export class SecurityAddCertModal extends React.Component {
isLoading={spinning}
spinnerAriaValueText={spinning ? "Saving" : undefined}
{...extraPrimaryProps}
isDisabled={certName === "" || (certRadioFile && certFile === "") ||
(certRadioText && (certText === "" || badCertText)) ||
(certRadioUpload && uploadValue === "")
isDisabled={
certName === "" || (certRadioFile && certFile === "") ||
(certRadioUpload && (uploadValue === "" || badCertText)) ||
(certRadioSelect && certNames.length === 0)
}
>
{saveBtnName}
Expand Down Expand Up @@ -247,9 +253,45 @@ export class SecurityAddCertModal extends React.Component {
</Grid>
<Grid className="ds-margin-top" >
<GridItem span={12}>
<div title="Upload the contents of a PEM file from the client's system.">
<Radio
id="certRadioUpload"
label={certTextLabel}
name="certChoice"
onChange={handleRadioChange}
isChecked={certRadioUpload}
/>
</div>
<div className={certRadioUpload ? "ds-margin-top ds-radio-indent" : "ds-margin-top ds-radio-indent ds-disabled"}>
<FileUpload
id="uploadPEMFile"
type="text"
value={uploadValue}
filename={uploadFileName}
filenamePlaceholder="Drag and drop a file, or upload one"
onFileInputChange={handleFileInputChange}
onDataChange={handleTextOrDataChange}
onTextChange={handleTextOrDataChange}
onReadStarted={handleFileReadStarted}
onReadFinished={handleFileReadFinished}
onClearClick={handleClear}
isLoading={uploadIsLoading}
dropzoneProps={{
accept: '.pem',
onDropRejected: handleFileRejected
}}
validated={
uploadIsRejected ||
(certRadioUpload && uploadValue === "") ||
(certRadioUpload && badCertText) ? 'error' : 'default'
}
browseButtonText="Upload PEM File"
/>
</div>
<div title="Choose a cerificate from the server's certificate directory">
<Radio
id="certRadioSelect"
className="ds-margin-top-lg"
label="Choose Cerificate From Server"
name="certChoice"
isChecked={certRadioSelect}
Expand All @@ -263,8 +305,18 @@ export class SecurityAddCertModal extends React.Component {
onChange={handleCertSelect}
aria-label="FormSelect Input"
className="ds-cert-select"
validated={selectValidated}
>
{certNames.map((option, index) => (
{certNames.length === 0 &&
<FormSelectOption
key="none"
value=""
label="No certificates present"
isDisabled
isPlaceholder
/>
}
{certNames.length > 0 && certNames.map((option, index) => (
<FormSelectOption
key={index}
value={option}
Expand Down Expand Up @@ -296,60 +348,8 @@ export class SecurityAddCertModal extends React.Component {
validated={certRadioFile && certFile === "" ? ValidatedOptions.error : ValidatedOptions.default}
/>
</div>
<Radio
id="certRadioText"
className="ds-margin-top-lg"
label={certTextLabel}
name="certChoice"
onChange={handleRadioChange}
isChecked={certRadioText}
/>
<div className={certRadioText ? "ds-margin-top ds-radio-indent" : "ds-margin-top ds-radio-indent ds-disabled"}>
<TextArea
id="certText"
value={certText}
onChange={(value, e) => {
handleChange(e);
}}
aria-label="cert text input"
validated={certRadioText && (certText === "" || badCertText) ? ValidatedOptions.error : ValidatedOptions.default}
/>
</div>
<div title="Upload the contents of a PEM file from the client system.">
<Radio
id="certRadioUpload"
className="ds-margin-top-lg"
label="Upload Local PEM File"
name="certChoice"
onChange={handleRadioChange}
isChecked={certRadioUpload}
/>
</div>
<div className={certRadioUpload ? "ds-margin-top ds-radio-indent" : "ds-margin-top ds-radio-indent ds-disabled"}>
<FileUpload
id="uploadPEMFile"
type="text"
value={uploadValue}
filename={uploadFileName}
filenamePlaceholder="Drag and drop a file, or upload one"
onFileInputChange={handleFileInputChange}
onDataChange={handleTextOrDataChange}
onTextChange={handleTextOrDataChange}
onReadStarted={handleFileReadStarted}
onReadFinished={handleFileReadFinished}
onClearClick={handleClear}
isLoading={uploadIsLoading}
dropzoneProps={{
accept: '.pem',
onDropRejected: handleFileRejected
}}
validated={uploadIsRejected || (certRadioUpload && uploadValue === "") ? 'error' : 'default'}
browseButtonText="Upload PEM File"
/>
</div>
</GridItem>
</Grid>

</Form>
</Modal>
);
Expand Down Expand Up @@ -411,7 +411,7 @@ export class SecurityAddCSRModal extends React.Component {
isLoading={spinning}
spinnerAriaValueText={spinning ? "Saving" : undefined}
{...extraPrimaryProps}
isDisabled={error.csrName || error.csrSubjectCommonName || spinning || !validAltNames}
isDisabled={error.csrName || bad_file_name(csrName) || error.csrSubjectCommonName || spinning || !validAltNames}
>
{saveBtnName}
</Button>,
Expand Down
Loading

0 comments on commit c0b8a21

Please sign in to comment.