diff --git a/models/error.go b/models/error.go index d11a9eeb1feb3..91ae697ed0d0b 100644 --- a/models/error.go +++ b/models/error.go @@ -229,6 +229,25 @@ func (err ErrKeyNameAlreadyUsed) Error() string { return fmt.Sprintf("public key already exists [owner_id: %d, name: %s]", err.OwnerID, err.Name) } +// ErrGPGKeyAccessDenied represents a "GPGKeyAccessDenied" kind of Error. +type ErrGPGKeyAccessDenied struct { + UserID int64 + KeyID int64 + Note string +} + +// IsErrGPGKeyAccessDenied checks if an error is a ErrGPGKeyAccessDenied. +func IsErrGPGKeyAccessDenied(err error) bool { + _, ok := err.(ErrGPGKeyAccessDenied) + return ok +} + +// Error pretty-prints an error of type ErrGPGKeyAccessDenied. +func (err ErrGPGKeyAccessDenied) Error() string { + return fmt.Sprintf("user does not have access to the key [user_id: %d, key_id: %d, note: %s]", + err.UserID, err.KeyID, err.Note) +} + // ErrKeyAccessDenied represents a "KeyAccessDenied" kind of error. type ErrKeyAccessDenied struct { UserID int64 @@ -242,6 +261,7 @@ func IsErrKeyAccessDenied(err error) bool { return ok } +// Error pretty-prints an error of type ErrKeyAccessDenied func (err ErrKeyAccessDenied) Error() string { return fmt.Sprintf("user does not have access to the key [user_id: %d, key_id: %d, note: %s]", err.UserID, err.KeyID, err.Note) diff --git a/models/gpg_key.go b/models/gpg_key.go new file mode 100644 index 0000000000000..0b96f1611cf70 --- /dev/null +++ b/models/gpg_key.go @@ -0,0 +1,105 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package models + +import ( + "time" + + "github.com/gogits/gogs/modules/log" +) + +//TODO maybe refector with ssh-key ? +//TODO database behind + +// PublicGPGKey represents a GPG key. +type PublicGPGKey struct { + ID int64 `xorm:"pk autoincr"` + OwnerID int64 `xorm:"INDEX NOT NULL"` + Name string `xorm:"NOT NULL"` + Fingerprint string `xorm:"NOT NULL"` + Content string `xorm:"TEXT NOT NULL"` + + Created time.Time `xorm:"-"` +} + +// ListPublicGPGKeys returns a list of public keys belongs to given user. +func ListPublicGPGKeys(uid int64) ([]*PublicGPGKey, error) { + keys := make([]*PublicGPGKey, 0, 5) + return keys, x.Where("owner_id=?", uid).Find(&keys) +} + +// GetPublicGPGKeyByID returns public key by given ID. +func GetPublicGPGKeyByID(keyID int64) (*PublicGPGKey, error) { + key := new(PublicGPGKey) + has, err := x.Id(keyID).Get(key) + if err != nil { + return nil, err + } else if !has { + return nil, ErrKeyNotExist{keyID} + } + return key, nil +} + +// CheckPublicGPGKeyString checks if the given public key string is a valid GPG key. +// The function returns the actual public key line on success. +func CheckPublicGPGKeyString(content string) (_ string, err error) { + //TODO Implement + return "", nil +} + +// AddPublicGPGKey adds new public key to database. +func AddPublicGPGKey(ownerID int64, name, content string) (*PublicKey, error) { + log.Trace(content) + //TODO Implement + return nil, nil +} + +// DeletePublicGPGKey deletes GPG key information in database. +func DeletePublicGPGKey(doer *User, id int64) (err error) { + //TODO Implement + return nil +} + +/* TODO +// CheckCommitWithSign checks if author's signature of commit is corresponsind to a user. +func CheckCommitWithSign(c *git.Commit) *User { + u, err := GetUserByEmail(c.Author.Email) + if err != nil { + return nil + } + ks, err := ListPublicGPGKeys(u.ID) + if err != nil { + return nil + } + return u +} + +// CheckCommitsWithSign checks if author's signature of commits are corresponding to users. +func CheckCommitsWithSign(oldCommits *list.List) *list.List { + var ( + u *User + emails = map[string]*User{} + newCommits = list.New() + e = oldCommits.Front() + ) + for e != nil { + c := e.Value.(*git.Commit) + + if v, ok := emails[c.Author.Email]; !ok { + u, _ = GetUserByEmail(c.Author.Email) + emails[c.Author.Email] = u + } else { + u = v + } + + newCommits.PushBack(UserCommit{ + User: u, + Commit: c, + }) + e = e.Next() + } + return newCommits +} +*/ diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 11adfa5c5f77b..4b1c0df9847b1 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -243,6 +243,13 @@ func RegisterRoutes(m *macaron.Macaron) { }) m.Get("/subscriptions", user.GetMyWatchedRepos) + + m.Group("/gpg_keys", func() { + m.Combo("").Get(user.ListMyPublicGPGKeys). + Post(bind(api.CreateKeyOption{}), user.CreatePublicGPGKey) //TODO use specific api descriptor + m.Combo("/:id").Get(user.GetPublicGPGKey). + Delete(user.DeletePublicGPGKey) + }) }, reqToken()) // Repositories diff --git a/routers/api/v1/convert/convert.go b/routers/api/v1/convert/convert.go index 9729e65452cfd..5c346ad1de39b 100644 --- a/routers/api/v1/convert/convert.go +++ b/routers/api/v1/convert/convert.go @@ -58,6 +58,14 @@ func ToCommit(c *git.Commit) *api.PayloadCommit { Email: c.Committer.Email, UserName: committerUsername, }, + /* TODO in api "github.com/gogits/go-gogs-client" + Verification: &api.PayloadVerification{ + Verified: true || c.Verification.Verified, //TODO check sign + Reason: "Not implemented", //TODO check sign + Signature: c.Verification.Signature, + Payload: c.Verification.Payload, + }, + */ Timestamp: c.Author.When, } } @@ -73,6 +81,18 @@ func ToPublicKey(apiLink string, key *models.PublicKey) *api.PublicKey { } } +// ToPublicGPGKey converts models.PublicGPGKey to api.PublicGPGKey +//TODO be more specific to GPG key with a api.PublicGPGKey ? +func ToPublicGPGKey(apiLink string, key *models.PublicGPGKey) *api.PublicKey { + return &api.PublicKey{ + ID: key.ID, + Key: key.Content, + URL: apiLink + com.ToStr(key.ID), + Title: key.Name, + Created: key.Created, + } +} + // ToHook convert models.Webhook to api.Hook func ToHook(repoLink string, w *models.Webhook) *api.Hook { config := map[string]string{ diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index 546cc40e6116f..2477a2011d765 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -63,6 +63,16 @@ func GetDeployKey(ctx *context.APIContext) { ctx.JSON(200, convert.ToDeployKey(apiLink, key)) } +// HandleCheckGPGKeyStringError handle check GPGKey error +func HandleCheckGPGKeyStringError(ctx *context.APIContext, err error) { + //TODO Implement +} + +// HandleAddGPGKeyError handle add GPGKey error +func HandleAddGPGKeyError(ctx *context.APIContext, err error) { + //TODO Implement +} + // HandleCheckKeyStringError handle check key error func HandleCheckKeyStringError(ctx *context.APIContext, err error) { if models.IsErrKeyUnableVerify(err) { diff --git a/routers/api/v1/user/gpg_keys.go b/routers/api/v1/user/gpg_keys.go new file mode 100644 index 0000000000000..a6f4821735060 --- /dev/null +++ b/routers/api/v1/user/gpg_keys.go @@ -0,0 +1,93 @@ +// Copyright 2015 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package user + +import ( + api "github.com/gogits/go-gogs-client" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/context" + "github.com/gogits/gogs/modules/setting" + "github.com/gogits/gogs/routers/api/v1/convert" + "github.com/gogits/gogs/routers/api/v1/repo" +) + +func composePublicGPGKeysAPILink() string { + return setting.AppUrl + "api/v1/user/gpg_keys/" +} + +func listPublicGPGKeys(ctx *context.APIContext, uid int64) { + keys, err := models.ListPublicGPGKeys(uid) + if err != nil { + ctx.Error(500, "ListPublicGPGKeys", err) + return + } + + apiLink := composePublicGPGKeysAPILink() + apiKeys := make([]*api.PublicKey, len(keys)) + for i := range keys { + apiKeys[i] = convert.ToPublicGPGKey(apiLink, keys[i]) + } + + ctx.JSON(200, &apiKeys) +} + +// https://github.com/gogits/go-gogs-client/wiki/Users-Public-GPG-Keys#list-your-public-keys +func ListMyPublicGPGKeys(ctx *context.APIContext) { + listPublicGPGKeys(ctx, ctx.User.ID) +} + +// https://github.com/gogits/go-gogs-client/wiki/Users-Public-GPG-Keys#get-a-single-public-key +func GetPublicGPGKey(ctx *context.APIContext) { + key, err := models.GetPublicGPGKeyByID(ctx.ParamsInt64(":id")) + if err != nil { + if models.IsErrKeyNotExist(err) { + ctx.Status(404) + } else { + ctx.Error(500, "GetPublicGPGKeyByID", err) + } + return + } + + apiLink := composePublicGPGKeysAPILink() + ctx.JSON(200, convert.ToPublicGPGKey(apiLink, key)) +} + +// CreateUserPublicGPGKey creates new public GPG key to given user by ID. +func CreateUserPublicGPGKey(ctx *context.APIContext, form api.CreateKeyOption, uid int64) { + content, err := models.CheckPublicGPGKeyString(form.Key) + if err != nil { + repo.HandleCheckGPGKeyStringError(ctx, err) + return + } + + key, err := models.AddPublicGPGKey(uid, form.Title, content) + if err != nil { + repo.HandleAddGPGKeyError(ctx, err) + return + } + apiLink := composePublicKeysAPILink() + ctx.JSON(201, convert.ToPublicKey(apiLink, key)) +} + +//TODO Update api +// https://github.com/gogits/go-gogs-client/wiki/Users-Public-GPG-Keys#create-a-public-key +func CreatePublicGPGKey(ctx *context.APIContext, form api.CreateKeyOption) { + CreateUserPublicGPGKey(ctx, form, ctx.User.ID) +} + +// https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#delete-a-public-key +func DeletePublicGPGKey(ctx *context.APIContext) { + if err := models.DeletePublicGPGKey(ctx.User, ctx.ParamsInt64(":id")); err != nil { + if models.IsErrGPGKeyAccessDenied(err) { + ctx.Error(403, "", "You do not have access to this key") + } else { + ctx.Error(500, "DeletePublicGPGKey", err) + } + return + } + + ctx.Status(204) +} diff --git a/routers/repo/commit.go b/routers/repo/commit.go index ff90cdf465a64..13d3dc74188bf 100644 --- a/routers/repo/commit.go +++ b/routers/repo/commit.go @@ -66,6 +66,7 @@ func Commits(ctx *context.Context) { } commits = renderIssueLinks(commits, ctx.Repo.RepoLink) commits = models.ValidateCommitsWithEmails(commits) + commits = models.CheckCommitsWithSign(commits) ctx.Data["Commits"] = commits ctx.Data["Username"] = ctx.Repo.Owner.Name diff --git a/routers/repo/view.go b/routers/repo/view.go index 7e7ad0b923ee8..aae137184fea5 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -45,6 +45,7 @@ func renderDirectory(ctx *context.Context, treeLink string) { entries.Sort() ctx.Data["Files"], err = entries.GetCommitsInfo(ctx.Repo.Commit, ctx.Repo.TreePath) + //TODO commits = models.CheckCommitsWithSign(commits) if err != nil { ctx.Handle(500, "GetCommitsInfo", err) return diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl index 4956acd07ec28..90221f74f20f7 100644 --- a/templates/repo/commits_table.tmpl +++ b/templates/repo/commits_table.tmpl @@ -41,7 +41,14 @@
+ {{end}} +