admin/pki: use symlink instead of DB to associate certificate to team

This commit is contained in:
nemunaire 2018-04-13 20:59:03 +02:00 committed by Pierre-Olivier Mercier
parent 73eb04bcf0
commit 156a87abc0
4 changed files with 83 additions and 46 deletions

View File

@ -6,10 +6,12 @@ import (
"errors"
"fmt"
"io/ioutil"
"log"
"math/big"
"os"
"path"
"time"
"strconv"
"srs.epita.fr/fic-server/admin/pki"
"srs.epita.fr/fic-server/libfic"
@ -26,7 +28,21 @@ func init() {
}))
router.GET("/api/teams/:tid/certificates", apiHandler(teamHandler(
func(team fic.Team, _ []byte) (interface{}, error) { return fic.GetTeamCertificates(team) })))
func(team fic.Team, _ []byte) (interface{}, error) {
if serials, err := pki.GetTeamSerials(TeamsDir, team.Id); err != nil {
return nil, err
} else {
var certs []fic.Certificate
for _, serial := range serials {
if cert, err := fic.GetCertificate(serial); err == nil {
certs = append(certs, cert)
} else {
log.Println("Unable to get back certificate, whereas an association exists on disk: ", err)
}
}
return certs, nil
}
})))
router.GET("/api/certs/", apiHandler(getCertificates))
router.POST("/api/certs/", apiHandler(generateClientCert))
@ -128,7 +144,7 @@ func generateClientCert(_ httprouter.Params, _ []byte) (interface{}, error) {
type CertExported struct {
Id string `json:"id"`
Creation time.Time `json:"creation"`
IdTeam *int64 `json:"id_team"`
IdTeam *uint64 `json:"id_team"`
Revoked *time.Time `json:"revoked"`
}
@ -137,8 +153,17 @@ func getCertificates(_ httprouter.Params, _ []byte) (interface{}, error) {
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})
for _, cert := range certificates {
dstLinkPath := path.Join(TeamsDir, pki.GetCertificateAssociation(cert.Id))
var idTeam *uint64 = nil
if lnk, err := os.Readlink(dstLinkPath); err == nil {
if tid, err := strconv.ParseUint(lnk, 10, 64); err == nil {
idTeam = &tid
}
}
ret = append(ret, CertExported{fmt.Sprintf("%d", cert.Id), cert.Creation, idTeam, cert.Revoked})
}
return ret, nil
}
@ -154,15 +179,7 @@ func updateCertificateAssociation(cert fic.Certificate, body []byte) (interface{
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
var serial big.Int
serial.SetUint64(cert.Id)
dstLinkPath := path.Join(TeamsDir, fmt.Sprintf("_AUTH_ID_%0X", serial.Bytes()))
dstLinkPath := path.Join(TeamsDir, pki.GetCertificateAssociation(cert.Id))
if uc.Team != nil {
srcLinkPath := fmt.Sprintf("%d", *uc.Team)
@ -173,9 +190,5 @@ func updateCertificateAssociation(cert fic.Certificate, body []byte) (interface{
os.Remove(dstLinkPath)
}
if _, err := cert.Update(); err != nil {
return nil, err
} else {
return cert, err
}
return cert, nil
}

46
admin/pki/team.go Normal file
View File

@ -0,0 +1,46 @@
package pki
import (
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
)
func GetCertificateAssociation(serial uint64) string {
return fmt.Sprintf("_AUTH_ID_%0X", serial)
}
func GetAssociations(dirname string) (assocs []string, err error) {
if ds, errr := ioutil.ReadDir(dirname); err != nil {
return nil, errr
} else {
for _, d := range ds {
if d.Mode() & os.ModeSymlink == os.ModeSymlink {
assocs = append(assocs, d.Name())
}
}
return
}
}
func GetTeamSerials(dirname string, id_team int64) (serials []uint64, err error) {
// As futher comparaisons will be made with strings, convert it only one time
str_tid := fmt.Sprintf("%d", id_team)
var assocs []string
if assocs, err = GetAssociations(dirname); err != nil {
return
} else {
for _, assoc := range assocs {
var tid string
if tid, err = os.Readlink(path.Join(dirname, assoc)); err == nil && tid == str_tid {
if serial, err := strconv.ParseUint(assoc[9:], 16, 64); err == nil {
serials = append(serials, serial)
}
}
}
}
return
}

View File

@ -20,20 +20,19 @@ type Certificate struct {
Id uint64 `json:"id,string"`
Creation time.Time `json:"creation"`
Password string `json:"password"`
IdTeam *int64 `json:"id_team"`
Revoked *time.Time `json:"revoked"`
}
// GetCertificates returns the list of all generated certificates.
func GetCertificates() (certificates []Certificate, err error) {
var rows *sql.Rows
if rows, err = DBQuery("SELECT id_cert, creation, password, id_team, revoked FROM certificates ORDER BY creation"); err == nil {
if rows, err = DBQuery("SELECT id_cert, creation, password, revoked FROM certificates ORDER BY creation"); err == nil {
defer rows.Close()
certificates = make([]Certificate, 0)
for rows.Next() {
var c Certificate
if err = rows.Scan(&c.Id, &c.Creation, &c.Password, &c.IdTeam, &c.Revoked); err != nil {
if err = rows.Scan(&c.Id, &c.Creation, &c.Password, &c.Revoked); err != nil {
return
}
certificates = append(certificates, c)
@ -44,28 +43,9 @@ func GetCertificates() (certificates []Certificate, err error) {
}
// GetTeamCertificates returns all certificates generated for a given Team.
func GetTeamCertificates(team Team) (certificates []Certificate, err error) {
var rows *sql.Rows
if rows, err = DBQuery("SELECT id_cert, creation, password, id_team, revoked FROM certificates WHERE id_team = ? ORDER BY creation", team.Id); err == nil {
defer rows.Close()
certificates = make([]Certificate, 0)
for rows.Next() {
var c Certificate
if err = rows.Scan(&c.Id, &c.Creation, &c.Password, &c.IdTeam, &c.Revoked); err != nil {
return
}
certificates = append(certificates, c)
}
err = rows.Err()
}
return
}
// GetCertificate retrieves a certificate from its serial number.
func GetCertificate(serial uint64) (c Certificate, err error) {
err = DBQueryRow("SELECT id_cert, creation, password, id_team, revoked FROM certificates WHERE id_cert = ?", serial).Scan(&c.Id, &c.Creation, &c.Password, &c.IdTeam, &c.Revoked)
err = DBQueryRow("SELECT id_cert, creation, password, revoked FROM certificates WHERE id_cert = ?", serial).Scan(&c.Id, &c.Creation, &c.Password, &c.Revoked)
return
}
@ -87,13 +67,13 @@ func RegisterCertificate(serial uint64, password string) (Certificate, error) {
if _, err := DBExec("INSERT INTO certificates (id_cert, creation, password) VALUES (?, ?, ?)", serial, now, password); err != nil {
return Certificate{}, err
} else {
return Certificate{serial, now, password, nil, nil}, nil
return Certificate{serial, now, password, nil}, nil
}
}
// Update applies modifications back to the database.
func (c Certificate) Update() (int64, error) {
if res, err := DBExec("UPDATE certificates SET creation = ?, password = ?, id_team = ?, revoked = ? WHERE id_cert = ?", c.Creation, c.Password, c.IdTeam, c.Revoked, c.Id); err != nil {
if res, err := DBExec("UPDATE certificates SET creation = ?, password = ?, revoked = ? WHERE id_cert = ?", c.Creation, c.Password, c.Revoked, c.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err

View File

@ -91,9 +91,7 @@ CREATE TABLE IF NOT EXISTS certificates(
id_cert BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
creation TIMESTAMP NOT NULL,
password VARCHAR(255) NOT NULL,
id_team INTEGER NULL,
revoked TIMESTAMP NULL,
FOREIGN KEY(id_team) REFERENCES teams(id_team)
revoked TIMESTAMP NULL
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
return err