kube CSR: preload UI fields

This commit is contained in:
Mikaël Cluseau
2026-07-20 12:20:47 +02:00
parent 4d1deeb773
commit d2aefba82e
5 changed files with 76 additions and 2 deletions
+38
View File
@@ -2,6 +2,8 @@ package main
import (
"bytes"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"log"
@@ -355,3 +357,39 @@ func parseCertDuration(d string, now time.Time) (t time.Time, err error) {
return
}
type KubeCSRParseReq struct {
CSR string `json:"csr"`
}
type KubeCSRParseResp struct {
User string `json:"user,omitempty"`
Group string `json:"group,omitempty"`
}
func wsParseKubeCSR(req *restful.Request, resp *restful.Response) {
var r KubeCSRParseReq
if err := req.ReadEntity(&r); err != nil {
wsError(resp, err)
return
}
block, _ := pem.Decode([]byte(r.CSR))
if block == nil || block.Type != "CERTIFICATE REQUEST" {
wsError(resp, fmt.Errorf("invalid CSR PEM"))
return
}
cr, err := x509.ParseCertificateRequest(block.Bytes)
if err != nil {
wsError(resp, fmt.Errorf("parse CSR: %w", err))
return
}
out := KubeCSRParseResp{User: cr.Subject.CommonName}
if len(cr.Subject.Organization) > 0 {
out.Group = cr.Subject.Organization[0]
}
resp.WriteAsJson(out)
}
+5 -1
View File
@@ -42,7 +42,11 @@ func registerWS(rest *restful.Container) {
Param(ws.PathParameter("asset", "the requested asset")).
Doc("Fetch an asset via a download token")).
Route(ws.GET("/download-set").To(wsDownloadSet)).
Route(ws.GET("/download-set/{kind}/{name}/{asset}").To(wsDownloadSetAsset))
Route(ws.GET("/download-set/{kind}/{name}/{asset}").To(wsDownloadSetAsset)).
Route(ws.POST("/parse-kube-csr").To(wsParseKubeCSR).
Reads(KubeCSRParseReq{}).
Writes(KubeCSRParseResp{}).
Doc("Parse a Kubernetes CSR PEM and return the subject fields"))
rest.Add(ws)
}
@@ -21,6 +21,22 @@ const ClusterAccess = {
msg: null,
};
},
watch: {
'kubeSignReq.CSR'(v) {
if (v && v.includes('BEGIN CERTIFICATE REQUEST')) {
fetch(`/public/parse-kube-csr`, {
method: 'POST',
body: JSON.stringify({ csr: v }),
headers: { 'Content-Type': 'application/json' },
}).then(r => {
if (r.ok) r.json().then(d => {
if (d.user) this.kubeSignReq.User = d.user;
if (d.group) this.kubeSignReq.Group = d.group;
});
}).catch(() => {});
}
},
},
computed: {
availableAssets() {
return [
+1 -1
View File
@@ -13,7 +13,7 @@
<script src="/ui/app-4f0c6935250753e9.js" defer integrity="sha384-SrAfGs5pnGcgL5E78t2MszNYmdkGME9yPgtPsDg61Rljw+dfiP+rabjPnrMkWDUH" type="module"></script>
<script src="/ui/Downloads-3c8cba0572aebfae.js" integrity="sha384-Pof/sPwhdqKGp7NUmYkDc0pSSyACabwLjkRRylZKHxRmiHBV3SGomPUMAWG9y7Ix"></script>
<script src="/ui/GetCopy-2e04b7b63750e25a.js" integrity="sha384-sNvTYXhjog3BkYL20RzrUWIG9VGyHNO8oEiP5oxL5f2WKmLj/03fdR79FgHfzlj+"></script>
<script src="/ui/ClusterAccess-8b4165435ba4fcac.js" integrity="sha384-C1mVnhsaxmn3Dpp1vtcBuctxxXI1Rlh+qmxicCIpNv7ukyTbB524pg0ZwhvAnfH7"></script>
<script src="/ui/ClusterAccess-1090c2ac32649bb9.js" integrity="sha384-xs5yHZycLFkyMIIULJMMeDk04sly74uUtO7qaagZcLLKos9YIOv0sEIJkXRveno3"></script>
<script src="/ui/ClusterCAs-4f0a381bfb0c1b88.js" integrity="sha384-QcSHKzEHNSCqyjFELXfYFAdfmIZZwJHDzPqI2prH47jHrkeomJD60rcEMLkVAO3o"></script>
<script src="/ui/Cluster-34a012ee0910827c.js" integrity="sha384-Q0sXP0WFJu4gHOI/8NMOGwpJvvFDNBR3T/vDCYxCWL2szAu4+sQuwHm81Jcad/ve"></script>
<script src="/ui/Host-61916516a854adff.js" integrity="sha384-/wh3KrC0sb4MT7ekO2U84rswxI42WSH/0jB4dbDaaGaGhX60xTEZHFsdQAf7UgTG"></script>
+16
View File
@@ -21,6 +21,22 @@ const ClusterAccess = {
msg: null,
};
},
watch: {
'kubeSignReq.CSR'(v) {
if (v && v.includes('BEGIN CERTIFICATE REQUEST')) {
fetch(`/public/parse-kube-csr`, {
method: 'POST',
body: JSON.stringify({ csr: v }),
headers: { 'Content-Type': 'application/json' },
}).then(r => {
if (r.ok) r.json().then(d => {
if (d.user) this.kubeSignReq.User = d.user;
if (d.group) this.kubeSignReq.Group = d.group;
});
}).catch(() => {});
}
},
},
computed: {
availableAssets() {
return [