mirror of
https://github.com/outbackdingo/labca.git
synced 2026-01-27 10:19:34 +00:00
355 lines
15 KiB
Cheetah
355 lines
15 KiB
Cheetah
{{ define "body" }}
|
|
</div>
|
|
<div class="col-md-6 col-sm-12">
|
|
{{with .CertificateInfo}}
|
|
<h3>{{ if .IsRoot }}<b>Root</b>{{ else }}<b>Issuer</b> (2nd level){{ end }} Certificate</h3>
|
|
{{ if not .IsRoot }}
|
|
Root Subject: <span id="root-subject-display">{{ .RootSubject }}</span><br><br>
|
|
{{ end }}
|
|
|
|
<ul class="nav nav-tabs">
|
|
<li class="{{ if eq .CreateType "generate" }}active{{ end }}">
|
|
<a data-toggle="tab" href="#generate">Generate</a>
|
|
</li>
|
|
<li class="{{ if eq .CreateType "import" }}active{{ end }}">
|
|
<a data-toggle="tab" href="#import">Import</a>
|
|
</li>
|
|
<li class="{{ if eq .CreateType "upload" }}active{{ end }}">
|
|
<a data-toggle="tab" href="#upload">Upload</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="tab-content">
|
|
<div class="tab-pane fade {{ if eq .CreateType "generate" }}active in{{ end }}" id="generate">
|
|
<br/>
|
|
<form role="form" class="form-cert" enctype="multipart/form-data" method="POST">
|
|
<input type="hidden" name="cert" value="{{ if .IsRoot }}root{{ else }}issuer{{ end }}">
|
|
<input type="hidden" name="createtype" value="generate">
|
|
<input type="hidden" name="keep-root-online" id="keep-root-online">
|
|
<input type="hidden" name="ack-rootkey" id="ack-rootkey">
|
|
<input type="hidden" name="rootkey" id="rootkey">
|
|
<input type="hidden" name="rootpassphrase" id="rootpassphrase">
|
|
{{ if not .IsRoot }}
|
|
<input type="hidden" name="root-subject" id="root-subject" value="{{ .RootSubject }}">
|
|
<input type="hidden" name="root-enddate" id="root-enddate" value="{{ .RootEnddate }}">
|
|
{{ end }}
|
|
<div class="form-group">
|
|
<label for="keytype">Key type and size:</label>
|
|
<select class="form-control" id="keytype" name="keytype" required autocomplete="off">
|
|
{{ $selected := .KeyType }}
|
|
{{ range $key, $value := .KeyTypes }}
|
|
<option value="{{ $key }}"{{ if eq $key $selected }} selected="selected"{{ end }}>{{ $value }}</option>
|
|
{{ end }}
|
|
</select>
|
|
{{ with .Errors.KeyType }}
|
|
<span class="error">{{ . }}</span>
|
|
{{ end }}
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="c">Country (2-Letter code):</label>
|
|
<input class="form-control" type="text" id="c" name="c" value="{{ .Country }}" maxlength="2" required {{ if and (ne .Country "") (.IsRootGenerated) }}readonly{{ end }}>
|
|
{{ with .Errors.Country }}
|
|
<span class="error">{{ . }}</span>
|
|
{{ end }}
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="o">Organization:</label>
|
|
<input class="form-control" type="text" id="o" name="o" value="{{ .Organization }}" required {{ if and (ne .Organization "") (.IsRootGenerated) }}readonly{{ end }}>
|
|
{{ with .Errors.Organization }}
|
|
<span class="error">{{ . }}</span>
|
|
{{ end }}
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="cn">Common Name:</label>
|
|
<input class="form-control" type="text" id="cn" name="cn" value="{{ .CommonName }}" required>
|
|
{{ with .Errors.CommonName }}
|
|
<span class="error">{{ . }}</span>
|
|
{{ end }}
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="numdays">Validity (days):</label>
|
|
<input class="form-control" type="text" id="numdays" name="numdays" value="{{ .NumDays }}" required>
|
|
End date: <span id="validity-enddate"></span><br>
|
|
<span class="error hidden" id="numdays-error">{{ .Errors.numdays }}</span>
|
|
</div>
|
|
<div>
|
|
{{ with .Errors.Generate }}
|
|
<span class="error">{{ . }}</span><br/>
|
|
{{ end }}
|
|
<input class="btn btn-default" type="submit" value="Generate" name="generate" id="generatebtn">
|
|
{{ if and (not .IsRoot) .IsFirst }}
|
|
<input class="btn btn-danger btn-right" type="submit" value="Revert Root" name="revertroot" id="revertroot">
|
|
{{ end }}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="tab-pane fade {{ if eq .CreateType "import" }}active in{{ end }}" id="import">
|
|
<br/>
|
|
<form role="form" class="form-cert" enctype="multipart/form-data" method="POST">
|
|
<input type="hidden" name="cert" value="{{ if .IsRoot }}root{{ else }}issuer{{ end }}">
|
|
<input type="hidden" name="createtype" value="import">
|
|
<p>
|
|
Here you can import a certificate that was exported from another LabCA instance, either in the .pfx (PKCS#12) or in the .zip format.<br/>
|
|
If you have separate key and certificate files, use the
|
|
<a href="#upload" onClick='$(".nav-tabs > li:nth-child(3) > a:nth-child(1)").click()'>Upload</a> tab.
|
|
</p>
|
|
<div class="form-group">
|
|
<label for="import">Certificate:
|
|
{{ with .Errors.Import }}
|
|
<span class="error"><br/>{{ . }}</span>
|
|
{{ end }}
|
|
</label>
|
|
<input class="form-control non-fluid" type="file" id="import" name="import" required/>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="import-pwd">File password (optional):
|
|
<input class="form-control non-fluid" type="password" id="import-pwd" name="import-pwd"/>
|
|
</div>
|
|
<div>
|
|
<input class="btn btn-default" type="submit" value="Import">
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="tab-pane fade {{ if eq .CreateType "upload" }}active in{{ end }}" id="upload">
|
|
<br/>
|
|
<form role="form" class="form-cert" enctype="multipart/form-data" method="POST">
|
|
<input type="hidden" name="cert" value="{{ if .IsRoot }}root{{ else }}issuer{{ end }}">
|
|
<input type="hidden" name="createtype" value="upload">
|
|
<div class="form-group">
|
|
<label for="certificate">Certificate (in PEM format):
|
|
{{ with .Errors.Certificate }}
|
|
<span class="error"><br/>{{ . }}</span>
|
|
{{ end }}
|
|
</label>
|
|
<textarea class="form-control" id="certificate" name="certificate" rows="10" cols="80" required>{{ .Certificate }}</textarea>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="key">Key (in PEM format):
|
|
{{ with .Errors.Key }}
|
|
<span class="error"><br/>{{ . }}</span>
|
|
{{ end }}
|
|
</label>
|
|
<textarea class="form-control" id="key" name="key" rows="10" cols="80" required>{{ .Key }}</textarea>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="passphrase">Passphrase (optional):
|
|
{{ with .Errors.Passphrase }}
|
|
<span class="error"><br/>{{ . }}</span>
|
|
{{ end }}
|
|
</label>
|
|
<input class="form-control" type="password" id="passphrase" name="passphrase" value="">
|
|
</div>
|
|
<div class="form-group" style="display: none;">
|
|
You should provide the Root CRL here as well, but this can also be done later<br>
|
|
<label for="crl">Root CRL (optional):
|
|
{{ with .Errors.CRL }}
|
|
<span class="error"><br/>{{ . }}</span>
|
|
{{ end }}
|
|
</label>
|
|
<textarea class="form-control" id="crl" name="crl" rows="10" cols="80">{{ .CRL }}</textarea>
|
|
</div>
|
|
<div>
|
|
{{ with .Errors.Upload }}
|
|
<span class="error">{{ . }}</span><br/>
|
|
{{ end }}
|
|
<input class="btn btn-default" type="submit" id="cert-submit" value="Upload">
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<p id="processing" class="hidden"><br/>Setting up LabCA and applying configuration. This will take a minute...<br/>
|
|
<img id="restart-spinner" src="static/img/spinner.gif" height="36"></p>
|
|
{{end}}
|
|
|
|
{{ if ne .RootKey nil }}
|
|
<div id="modal-rootkey" class="modal fade" data-backdrop="static" data-keyboard="false" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content modal-rootkey-content">
|
|
<h4>Root Key</h4>
|
|
<p>This is the generated private key for the Root CA certificate. <span class="error"><b>Copy it now and store
|
|
it in a secure, private and offline location!</b></span> After setting up the Issuer CA certificate in the next
|
|
step, we will delete the key from the system.<br/>
|
|
When we need the key for certain operations in the future, we will ask for it.</p>
|
|
<textarea class="form-control" rows="10" cols="80" readonly>{{ .RootKey }}</textarea>
|
|
<input type="checkbox" id="modal-keep-root-online"> Keep the root key on the LabCA server (INSECURE)<br/>
|
|
{{ with .CertificateInfo.Errors.Modal }}
|
|
<span class="error">{{ . }}</span><br/>
|
|
{{ end }}
|
|
<input class="btn btn-default btn-reg" value="OK" id="modal-rootkey-done"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
|
|
{{ if and (not .CertificateInfo.IsRoot) (ne .CSR nil) }}
|
|
<div id="modal-csr" class="modal fade" data-backdrop="static" data-keyboard="false" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content modal-csr-content">
|
|
<h4>CSR</h4>
|
|
<textarea class="form-control" rows="10" cols="80" readonly>{{ .CSR }}</textarea>
|
|
<p>Please have this CSR signed by the offline Root CA. Then you can close this dialog and upload the resulting certificate.</p>
|
|
{{ with .CertificateInfo.Errors.Modal }}
|
|
<span class="error">{{ . }}</span><br/>
|
|
{{ end }}
|
|
<input class="btn btn-default btn-reg" value="OK" id="modal-csr-done"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
|
|
{{ if .GetRootKey }}
|
|
<div id="modal-root-key" class="modal fade" data-backdrop="static" data-keyboard="false" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content modal-root-key-content">
|
|
<h4>Root Key Required</h4>
|
|
<p>Please provide the Root CA key file in PEM format. As soon as we are done with it, it will be removed from the system again.</p>
|
|
<textarea class="form-control" id="modal-rootkey" rows="10" cols="80"></textarea>
|
|
<div class="form-group">
|
|
<label for="modal-rootpassphrase">Passphrase (optional):</label>
|
|
<input class="form-control" type="password" id="modal-rootpassphrase" value="">
|
|
</div>
|
|
{{ with .CertificateInfo.Errors.Modal }}
|
|
<span class="error">{{ . }}</span><br/>
|
|
{{ end }}
|
|
<input class="btn btn-default btn-reg" value="Upload" id="modal-root-key-upload"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
|
|
{{ template "partials/progress.tmpl" . }}
|
|
{{end}}
|
|
|
|
{{ define "tail" }}
|
|
<script>
|
|
$(function() {
|
|
$("ul.nav").click(function() {
|
|
setTimeout(function() {
|
|
$(window).resize();
|
|
}, 250);
|
|
});
|
|
|
|
{{ if ne .CSR nil }}
|
|
$("#modal-csr").modal("show");
|
|
{{ end }}
|
|
$("#modal-csr-done").click(function() {
|
|
$('.nav-tabs a[href="#upload"]').tab('show');
|
|
$("#key").val("").parent().hide();
|
|
$("#passphrase").val("").parent().hide();
|
|
$("#crl").val("").parent().show();
|
|
$("#modal-csr").modal("hide");
|
|
});
|
|
|
|
{{ if ne .RootKey nil }}
|
|
$("#modal-rootkey").modal("show");
|
|
{{ end }}
|
|
$("#modal-rootkey-done").click(function() {
|
|
$("#ack-rootkey").val("yes");
|
|
$("#keep-root-online").val($("#modal-keep-root-online").prop("checked"));
|
|
$("#modal-rootkey").modal("hide");
|
|
$("#generatebtn").click();
|
|
});
|
|
|
|
{{ if .GetRootKey }}
|
|
$("#modal-root-key").modal("show");
|
|
{{ end }}
|
|
$("#modal-root-key-upload").click(function() {
|
|
$("#rootkey").val($("#modal-rootkey").val());
|
|
$("#rootpassphrase").val($("#modal-rootpassphrase").val());
|
|
$("#modal-root-key").modal("hide");
|
|
$("#generatebtn").click();
|
|
});
|
|
|
|
$("#revertroot").click(function() {
|
|
if (!window.confirm("Do you really want to remove the Root CA certificate?")) {
|
|
return false;
|
|
}
|
|
$("#c").removeAttr("required");
|
|
$("#o").removeAttr("required");
|
|
});
|
|
|
|
{{ if not .CertificateInfo.IsRoot }}
|
|
$(".form-cert").submit(function() {
|
|
$("#processing").removeClass("hidden");
|
|
});
|
|
{{end}}
|
|
|
|
function updateEndDate() {
|
|
// Determine new end date
|
|
x = new Date();
|
|
x.setDate(x.getDate() + parseInt($('#numdays').val()))
|
|
$('#validity-enddate').text(x.toUTCString());
|
|
|
|
try {
|
|
if (($("#root-enddate").val() != undefined) && ($("#root-enddate").val().length > 0)) {
|
|
r = new Date($("#root-enddate").val());
|
|
if (x > r) {
|
|
$("#numdays-error").text("Validity cannot exceed root certificate end date");
|
|
$("#numdays-error").removeClass('hidden').show();
|
|
} else {
|
|
$("#numdays-error").text("");
|
|
if (!$("#numdays-error").hasClass('hidden')) {
|
|
$("#numdays-error").hide().addClass('hidden');
|
|
}
|
|
}
|
|
} else {
|
|
$("#numdays-error").text("");
|
|
if (!$("#numdays-error").hasClass('hidden')) {
|
|
$("#numdays-error").hide().addClass('hidden');
|
|
}
|
|
}
|
|
} catch(e) {
|
|
console.log(e);
|
|
}
|
|
|
|
$("#generatebtn").prop('disabled', !$("#numdays-error").hasClass('hidden'));
|
|
}
|
|
|
|
$.fn.inputFilter = function(callback, errMsg) {
|
|
return this.on("input keydown keyup mousedown mouseup select contextmenu drop focusout", function(e) {
|
|
if (callback(this.value)) {
|
|
// Accepted value
|
|
if (["keydown","mousedown","focusout"].indexOf(e.type) >= 0){
|
|
$("#numdays-error").text("");
|
|
if (!$("#numdays-error").hasClass('hidden')) {
|
|
$("#numdays-error").hide().addClass('hidden');
|
|
}
|
|
}
|
|
this.oldValue = this.value;
|
|
this.oldSelectionStart = this.selectionStart;
|
|
this.oldSelectionEnd = this.selectionEnd;
|
|
updateEndDate();
|
|
} else if (this.hasOwnProperty("oldValue")) {
|
|
// Rejected value - restore the previous one
|
|
$("#numdays-error").text(errMsg);
|
|
if ($("#numdays-error").hasClass('hidden')) {
|
|
$("#numdays-error").removeClass('hidden').show();
|
|
}
|
|
this.value = this.oldValue;
|
|
this.setSelectionRange(this.oldSelectionStart, this.oldSelectionEnd);
|
|
} else {
|
|
// Rejected value - nothing to restore
|
|
this.value = "";
|
|
}
|
|
});
|
|
};
|
|
|
|
$("#numdays").inputFilter(function(value) {
|
|
return /^\d*$/.test(value); // Allow digits only, using a RegExp
|
|
},"Only digits are allowed");
|
|
|
|
$("#certificate").change(function() {
|
|
$("#cert-submit").prop('disabled', ($("#certificate").val() == "" && $("#key").val() == ""));
|
|
})
|
|
|
|
$("#key").change(function() {
|
|
$("#cert-submit").prop('disabled', ($("#certificate").val() == "" && $("#key").val() == ""));
|
|
})
|
|
|
|
updateEndDate();
|
|
});
|
|
</script>
|
|
{{end}}
|