package main import ( "bytes" "fmt" "log" "net/http" "net/mail" "strconv" "time" "github.com/ProtonMail/go-crypto/openpgp" "github.com/gin-gonic/gin" ) func declareAPIKeysRoutes(router *gin.RouterGroup) { usersRoutes := router.Group("/users/:uid") usersRoutes.Use(userHandler) usersRoutes.GET("/pgp_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 []byte if len(keys) == 0 { ret, err = GitLab_getUserPGPKeys(c.Request.Context(), u) if err != nil { log.Println("Unable to GitLab_getUserPGPKeys:", err) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve your keys from GitLab. Please try again in a few moment or add them directly on /keys page on this website."}) return } } else { for _, key := range keys { if key.Type == "pgp" { ret = append(ret, []byte(key.Content)...) } } } c.Data(http.StatusOK, "application/pgp-keys", ret) }) usersRoutes.GET("/allowed_signers", func(c *gin.Context) { var u *User if user, ok := c.Get("user"); ok { u = user.(*User) } else { u = c.MustGet("LoggedUser").(*User) } user, err := GitLab_getUser(c.Request.Context(), u) if err != nil { log.Println("Unable to GitLab_getUser:", err) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve your GitLab user. Please try again in a few moment."}) return } keys, err := GitLab_getUserSSHKeys(c.Request.Context(), u) if err != nil { log.Println("Unable to GitLab_getUserSSHKeys:", err) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve your keys from GitLab. Please try again in a few moment."}) return } var ret string for _, k := range keys { if k.UsageType != "auth_and_signing" && k.UsageType != "signing" { continue } if len(user.Email) > 0 { ret += fmt.Sprintf("%s %s\n", user.Email, k.Key) } else { ret += fmt.Sprintf("*@epita.fr %s\n", k.Key) } } c.Data(http.StatusOK, "text/plain", []byte(ret)) }) } 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 } k2 := key if err := k2.ReadInfos(u); 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.Use(keyOnlyMyHandler) 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 { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to read your public key: %s", err.Error())}) return } c.JSON(http.StatusOK, k) }) keysRoutes.GET("export", func(c *gin.Context) { c.JSON(http.StatusOK, c.MustGet("key").(*Key)) }) 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 c.MustGet("LoggedUser").(*User).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() } } func keyOnlyMyHandler(c *gin.Context) { u := c.MustGet("LoggedUser").(*User) k := c.MustGet("key").(*Key) if u.IsAdmin { c.Next() } else if k.IdUser == u.Id { c.Next() } else { c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Key not found."}) return } } 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 } }