server/admin/api/certificate.go

168 lines
4.8 KiB
Go

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
}
}