Introducing new PKI management
This commit is contained in:
parent
5b558bcf00
commit
c118035c33
19 changed files with 857 additions and 53 deletions
|
@ -1,7 +1,167 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/pki"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
router.GET("/api/ca/", apiHandler(infoCA))
|
||||
router.GET("/api/ca.pem", apiHandler(getCAPEM))
|
||||
router.POST("/api/ca/new", apiHandler(
|
||||
func(_ httprouter.Params, _ []byte) (interface{}, error) { return true, pki.GenerateCA(time.Date(2018, 01, 21, 0, 0, 0, 0, time.UTC), time.Date(2018, 01, 24, 23, 59, 59, 0, time.UTC)) }))
|
||||
|
||||
router.GET("/api/teams/:tid/certificates", apiHandler(teamHandler(
|
||||
func(team fic.Team, _ []byte) (interface{}, error) { return fic.GetTeamCertificates(team) })))
|
||||
|
||||
router.GET("/api/certs/", apiHandler(getCertificates))
|
||||
router.POST("/api/certs/", apiHandler(generateClientCert))
|
||||
router.DELETE("/api/certs/", apiHandler(func(_ httprouter.Params, _ []byte) (interface{}, error) { return fic.ClearCertificates() }))
|
||||
|
||||
router.HEAD("/api/certs/:certid", apiHandler(certificateHandler(getTeamP12File)))
|
||||
router.GET("/api/certs/:certid", apiHandler(certificateHandler(getTeamP12File)))
|
||||
router.PUT("/api/certs/:certid", apiHandler(certificateHandler(updateCertificateAssociation)))
|
||||
router.DELETE("/api/certs/:certid", apiHandler(certificateHandler(
|
||||
func(cert fic.Certificate, _ []byte) (interface{}, error) { return cert.Revoke() })))
|
||||
}
|
||||
|
||||
func infoCA(_ httprouter.Params, _ []byte) (interface{}, error) {
|
||||
_, cacert, err := pki.LoadCA()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := map[string]interface{}{}
|
||||
|
||||
ret["version"] = cacert.Version
|
||||
ret["serialnumber"] = cacert.SerialNumber
|
||||
ret["issuer"] = cacert.Issuer
|
||||
ret["subject"] = cacert.Subject
|
||||
ret["notbefore"] = cacert.NotBefore
|
||||
ret["notafter"] = cacert.NotAfter
|
||||
ret["signatureAlgorithm"] = cacert.SignatureAlgorithm
|
||||
ret["publicKeyAlgorithm"] = cacert.PublicKeyAlgorithm
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getCAPEM(_ httprouter.Params, _ []byte) (interface{}, error) {
|
||||
if _, err := os.Stat(pki.CACertPath()); os.IsNotExist(err) {
|
||||
return nil, errors.New("Unable to locate the CA root certificate. Have you generated it?")
|
||||
} else if fd, err := os.Open(pki.CACertPath()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer fd.Close()
|
||||
return ioutil.ReadAll(fd)
|
||||
}
|
||||
}
|
||||
|
||||
func getTeamP12File(cert fic.Certificate, _ []byte) (interface{}, error) {
|
||||
// Create p12 if necessary
|
||||
if _, err := os.Stat(pki.ClientP12Path(cert.Id)); os.IsNotExist(err) {
|
||||
if err := pki.WriteP12(cert.Id, cert.Password); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(pki.ClientP12Path(cert.Id)); os.IsNotExist(err) {
|
||||
return nil, errors.New("Unable to locate the p12. Have you generated it?")
|
||||
} else if fd, err := os.Open(pki.ClientP12Path(cert.Id)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer fd.Close()
|
||||
return ioutil.ReadAll(fd)
|
||||
}
|
||||
}
|
||||
|
||||
func generateClientCert(_ httprouter.Params, _ []byte) (interface{}, error) {
|
||||
// First, generate a new, unique, serial
|
||||
serial := rand.Int63()
|
||||
for fic.ExistingCertSerial(serial) {
|
||||
serial = rand.Int63()
|
||||
}
|
||||
|
||||
// Let's pick a random password
|
||||
password, err := pki.GeneratePassword()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ok, now load CA
|
||||
capriv, cacert, err := pki.LoadCA()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate our privkey
|
||||
if err := pki.GenerateClient(serial, cacert.NotBefore, cacert.NotAfter, &cacert, &capriv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Save in DB
|
||||
return fic.RegisterCertificate(serial, password)
|
||||
}
|
||||
|
||||
type CertExported struct {
|
||||
Id string `json:"id"`
|
||||
Creation time.Time `json:"creation"`
|
||||
IdTeam *int64 `json:"id_team"`
|
||||
Revoked *time.Time `json:"revoked"`
|
||||
}
|
||||
|
||||
func getCertificates(_ httprouter.Params, _ []byte) (interface{}, error) {
|
||||
if certificates, err := fic.GetCertificates(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ret := make([]CertExported, 0)
|
||||
for _, c := range certificates {
|
||||
ret = append(ret, CertExported{fmt.Sprintf("%d", c.Id), c.Creation, c.IdTeam, c.Revoked})
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type CertUploaded struct {
|
||||
Team *int64 `json:"id_team"`
|
||||
}
|
||||
|
||||
func updateCertificateAssociation(cert fic.Certificate, body []byte) (interface{}, error) {
|
||||
var uc CertUploaded
|
||||
if err := json.Unmarshal(body, &uc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: This should be read from file system, not in DB:
|
||||
// the relation is made through a symlink, so if it exists, it is suffisant to read the relation
|
||||
// moreover, backend doesn't update the DB at registration, it only creates a symlink
|
||||
cert.IdTeam = uc.Team
|
||||
|
||||
dstLinkPath := path.Join(TeamsDir, fmt.Sprintf("_AUTH_ID_%X", cert.Id))
|
||||
|
||||
if uc.Team != nil {
|
||||
srcLinkPath := fmt.Sprintf("%d", *uc.Team)
|
||||
if err := os.Symlink(srcLinkPath, dstLinkPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
os.Remove(dstLinkPath)
|
||||
}
|
||||
|
||||
if _, err := cert.Update(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return cert, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ func showClaim(claim fic.Claim, _ []byte) (interface{}, error) {
|
|||
|
||||
type ClaimUploaded struct {
|
||||
Subject string `json:"subject"`
|
||||
Team *int `json:"id_team"`
|
||||
Team *int64 `json:"id_team"`
|
||||
Assignee *int64 `json:"id_assignee"`
|
||||
Priority string `json:"priority"`
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ func apiHandler(f DispatchFunction) func(http.ResponseWriter, *http.Request, htt
|
|||
|
||||
func teamPublicHandler(f func(*fic.Team,[]byte) (interface{}, error)) func (httprouter.Params,[]byte) (interface{}, error) {
|
||||
return func (ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if tid, err := strconv.Atoi(string(ps.ByName("tid"))); err != nil {
|
||||
if tid, err := strconv.ParseInt(string(ps.ByName("tid")), 10, 64); err != nil {
|
||||
return nil, err
|
||||
} else if tid == 0 {
|
||||
return f(nil, body)
|
||||
|
@ -98,7 +98,7 @@ func teamPublicHandler(f func(*fic.Team,[]byte) (interface{}, error)) func (http
|
|||
|
||||
func teamHandler(f func(fic.Team,[]byte) (interface{}, error)) func (httprouter.Params,[]byte) (interface{}, error) {
|
||||
return func (ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if tid, err := strconv.Atoi(string(ps.ByName("tid"))); err != nil {
|
||||
if tid, err := strconv.ParseInt(string(ps.ByName("tid")), 10, 64); err != nil {
|
||||
return nil, err
|
||||
} else if team, err := fic.GetTeam(tid); err != nil {
|
||||
return nil, err
|
||||
|
@ -280,6 +280,18 @@ func fileHandler(f func(fic.EFile,[]byte) (interface{}, error)) func (httprouter
|
|||
}
|
||||
}
|
||||
|
||||
func certificateHandler(f func(fic.Certificate,[]byte) (interface{}, error)) func (httprouter.Params,[]byte) (interface{}, error) {
|
||||
return func (ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if certid, err := strconv.ParseInt(string(ps.ByName("certid")), 10, 64); err != nil {
|
||||
return nil, err
|
||||
} else if cert, err := fic.GetCertificate(certid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(cert, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func notFound(ps httprouter.Params, _ []byte) (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
Reference in a new issue