Pierre-Olivier Mercier
8251fb2f96
All checks were successful
continuous-integration/drone/push Build is passing
309 lines
7.8 KiB
Go
309 lines
7.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"net/mail"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/ProtonMail/go-crypto/openpgp"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func declareAPIAuthKeysRoutes(router *gin.RouterGroup) {
|
|
router.GET("/keys", func(c *gin.Context) {
|
|
var u *User
|
|
if user, ok := c.Get("user"); ok {
|
|
u = user.(*User)
|
|
} else {
|
|
u = c.MustGet("LoggedUser").(*User)
|
|
}
|
|
|
|
keys, err := u.GetKeys()
|
|
if err != nil {
|
|
log.Println("Unable to GetKeys:", err)
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve your keys. Please try again in a few moment."})
|
|
return
|
|
}
|
|
|
|
var ret []int64
|
|
|
|
for _, key := range keys {
|
|
ret = append(ret, key.Id)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, ret)
|
|
})
|
|
router.POST("/keys", func(c *gin.Context) {
|
|
var u *User
|
|
if user, ok := c.Get("user"); ok {
|
|
u = user.(*User)
|
|
} else {
|
|
u = c.MustGet("LoggedUser").(*User)
|
|
}
|
|
|
|
var key Key
|
|
if err := c.ShouldBindJSON(&key); err != nil {
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
|
return
|
|
}
|
|
|
|
if err := key.CheckKey(); err != nil {
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
|
return
|
|
}
|
|
|
|
k, err := u.NewKey(key.Type, key.Content)
|
|
if err != nil {
|
|
log.Println("Unable to NewKey:", err)
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to register your public key. Please try again in a few moment."})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, k)
|
|
})
|
|
|
|
keysRoutes := router.Group("/keys/:kid")
|
|
keysRoutes.Use(keyHandler)
|
|
|
|
keysRoutes.GET("", func(c *gin.Context) {
|
|
var u *User
|
|
if user, ok := c.Get("user"); ok {
|
|
u = user.(*User)
|
|
} else {
|
|
u = c.MustGet("LoggedUser").(*User)
|
|
}
|
|
|
|
k := c.MustGet("key").(*Key)
|
|
|
|
if err := k.ReadInfos(u); err != nil {
|
|
log.Println("Unable to ReadInfos:", err)
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to read your public key. Please try again in a few moment."})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, k)
|
|
})
|
|
keysRoutes.PUT("", func(c *gin.Context) {
|
|
current := c.MustGet("key").(*Key)
|
|
|
|
var new Key
|
|
if err := c.ShouldBindJSON(&new); err != nil {
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
|
return
|
|
}
|
|
|
|
new.Id = current.Id
|
|
|
|
u := c.MustGet("LoggedUser").(*User)
|
|
if new.IdUser != current.IdUser && !u.IsAdmin {
|
|
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Operation not allowed."})
|
|
return
|
|
}
|
|
|
|
if key, err := new.Update(); err != nil {
|
|
log.Println("Unable to Update key:", err)
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs during key updation: %s", err.Error())})
|
|
return
|
|
} else {
|
|
c.JSON(http.StatusOK, key)
|
|
}
|
|
})
|
|
keysRoutes.DELETE("", func(c *gin.Context) {
|
|
key := c.MustGet("key").(*Key)
|
|
|
|
if _, err := key.Delete(); err != nil {
|
|
log.Println("Unable to Delete key:", err)
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs during key deletion: %s", err.Error())})
|
|
return
|
|
} else {
|
|
c.JSON(http.StatusOK, nil)
|
|
}
|
|
})
|
|
}
|
|
|
|
func keyHandler(c *gin.Context) {
|
|
var u *User
|
|
if user, ok := c.Get("user"); ok {
|
|
u = user.(*User)
|
|
} else {
|
|
u = c.MustGet("LoggedUser").(*User)
|
|
}
|
|
|
|
if kid, err := strconv.Atoi(string(c.Param("kid"))); err != nil {
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Bad key identifier."})
|
|
return
|
|
} else if u.IsAdmin {
|
|
if key, err := getKey(kid); err != nil {
|
|
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Key not found."})
|
|
return
|
|
} else {
|
|
c.Set("key", key)
|
|
c.Next()
|
|
}
|
|
} else if key, err := u.getKey(kid); err != nil {
|
|
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Key not found."})
|
|
return
|
|
} else {
|
|
c.Set("key", key)
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
type Key struct {
|
|
Id int64 `json:"id"`
|
|
IdUser int64 `json:"id_user"`
|
|
Type string `json:"type"`
|
|
Content string `json:"key,omitempty"`
|
|
Time time.Time `json:"time"`
|
|
Infos map[string]interface{} `json:"infos,omitempty"`
|
|
}
|
|
|
|
func (u *User) GetKeys() (keys []*Key, err error) {
|
|
if rows, errr := DBQuery("SELECT id_key, id_user, type, content, time FROM user_keys WHERE id_user=?", u.Id); errr != nil {
|
|
return nil, errr
|
|
} else {
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var k Key
|
|
if err = rows.Scan(&k.Id, &k.IdUser, &k.Type, &k.Content, &k.Time); err != nil {
|
|
return
|
|
}
|
|
keys = append(keys, &k)
|
|
}
|
|
if err = rows.Err(); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
func getKey(id int) (k *Key, err error) {
|
|
k = new(Key)
|
|
err = DBQueryRow("SELECT id_key, id_user, type, content, time FROM user_keys WHERE id_key=?", id).Scan(&k.Id, &k.IdUser, &k.Type, &k.Content, &k.Time)
|
|
return
|
|
}
|
|
|
|
func (u *User) getKey(id int) (k *Key, err error) {
|
|
k = new(Key)
|
|
err = DBQueryRow("SELECT id_key, id_user, type, content, time FROM user_keys WHERE id_key=? AND id_user=?", id, u.Id).Scan(&k.Id, &k.IdUser, &k.Type, &k.Content, &k.Time)
|
|
return
|
|
}
|
|
|
|
func (u *User) NewKey(kind, content string) (*Key, error) {
|
|
if res, err := DBExec("INSERT INTO user_keys (id_user, type, content) VALUES (?, ?, ?)", u.Id, kind, content); err != nil {
|
|
return nil, err
|
|
} else if kid, err := res.LastInsertId(); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return &Key{kid, u.Id, kind, content, time.Now(), nil}, nil
|
|
}
|
|
}
|
|
|
|
func (k *Key) CheckKey() error {
|
|
if k.Type == "pgp" {
|
|
keys, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(k.Content))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(keys) != 1 {
|
|
return fmt.Errorf("This is not a single public key file.")
|
|
}
|
|
|
|
if keys[0].PrivateKey != nil {
|
|
return fmt.Errorf("You send your PRIVATE key along with your public key. YOUR PRIVATE KEY IS COMPROMISED. Please revoke and regenerate a new key pair.")
|
|
}
|
|
|
|
if keys[0].Revocations != nil {
|
|
return fmt.Errorf("Your key seems to be revoked.")
|
|
}
|
|
|
|
return nil
|
|
} else {
|
|
return fmt.Errorf("%q is not a valid key type.", k.Type)
|
|
}
|
|
}
|
|
|
|
func (k *Key) ReadInfos(u *User) error {
|
|
if k.Type == "pgp" {
|
|
keys, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(k.Content))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
k.Content = ""
|
|
k.Infos = map[string]interface{}{}
|
|
|
|
var std_identity *openpgp.Identity
|
|
for name, idt := range keys[0].Identities {
|
|
if idt.Revoked(time.Now()) {
|
|
continue
|
|
}
|
|
|
|
address, err := mail.ParseAddress(name)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if address.Address == u.Email {
|
|
std_identity = idt
|
|
break
|
|
}
|
|
}
|
|
|
|
if std_identity == nil {
|
|
return fmt.Errorf("No identity found with %s email address.", u.Email)
|
|
}
|
|
|
|
if std_identity.UserId != nil {
|
|
k.Infos["identity"] = std_identity.UserId.Name
|
|
k.Infos["email"] = std_identity.UserId.Email
|
|
k.Infos["comment"] = std_identity.UserId.Comment
|
|
}
|
|
|
|
if std_identity.SelfSignature != nil {
|
|
k.Infos["keyid"] = fmt.Sprintf("%X", std_identity.SelfSignature.IssuerFingerprint)
|
|
k.Infos["sigexpired"] = std_identity.SelfSignature.SigExpired(time.Now())
|
|
k.Infos["creation"] = std_identity.SelfSignature.CreationTime
|
|
}
|
|
|
|
return nil
|
|
} else {
|
|
return fmt.Errorf("%q is not a valid key type.", k.Type)
|
|
}
|
|
}
|
|
|
|
func (k *Key) Update() (*Key, error) {
|
|
if _, err := DBExec("UPDATE user_keys SET id_user = ?, type = ?, content = ? WHERE id_key = ?", k.IdUser, k.Type, k.Content, k.Content, k.Id); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return k, err
|
|
}
|
|
}
|
|
|
|
func (k Key) Delete() (int64, error) {
|
|
if res, err := DBExec("DELETE FROM user_keys WHERE id_key = ?", k.Id); err != nil {
|
|
return 0, err
|
|
} else if nb, err := res.RowsAffected(); err != nil {
|
|
return 0, err
|
|
} else {
|
|
return nb, err
|
|
}
|
|
}
|
|
|
|
func ClearKeys() (int64, error) {
|
|
if res, err := DBExec("DELETE FROM user_keys"); err != nil {
|
|
return 0, err
|
|
} else if nb, err := res.RowsAffected(); err != nil {
|
|
return 0, err
|
|
} else {
|
|
return nb, err
|
|
}
|
|
}
|