gitea/modules/convert/convert.go

361 lines
11 KiB
Go
Raw Normal View History

// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2015-12-17 15:28:47 +08:00
package convert
import (
"fmt"
"strconv"
"strings"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631) This PR fixes #7598 by providing a configurable way of signing commits across the Gitea instance. Per repository configurability and import/generation of trusted secure keys is not provided by this PR - from a security PoV that's probably impossible to do properly. Similarly web-signing, that is asking the user to sign something, is not implemented - this could be done at a later stage however. ## Features - [x] If commit.gpgsign is set in .gitconfig sign commits and files created through repofiles. (merges should already have been signed.) - [x] Verify commits signed with the default gpg as valid - [x] Signer, Committer and Author can all be different - [x] Allow signer to be arbitrarily different - We still require the key to have an activated email on Gitea. A more complete implementation would be to use a keyserver and mark external-or-unactivated with an "unknown" trust level icon. - [x] Add a signing-key.gpg endpoint to get the default gpg pub key if available - Rather than add a fake web-flow user I've added this as an endpoint on /api/v1/signing-key.gpg - [x] Try to match the default key with a user on gitea - this is done at verification time - [x] Make things configurable? - app.ini configuration done - [x] when checking commits are signed need to check if they're actually verifiable too - [x] Add documentation I have decided that adjusting the docker to create a default gpg key is not the correct thing to do and therefore have not implemented this.
2019-10-16 21:42:42 +08:00
"code.gitea.io/gitea/modules/structs"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/webhook"
)
2016-11-24 15:04:31 +08:00
// ToEmail convert models.EmailAddress to api.Email
func ToEmail(email *models.EmailAddress) *api.Email {
2015-12-16 11:57:18 +08:00
return &api.Email{
Email: email.Email,
Verified: email.IsActivated,
Primary: email.IsPrimary,
}
}
// ToBranch convert a git.Commit and git.Branch to an api.Branch
func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.ProtectedBranch, user *models.User, isRepoAdmin bool) (*api.Branch, error) {
if bp == nil {
var hasPerm bool
var err error
if user != nil {
hasPerm, err = models.HasAccessUnit(user, repo, models.UnitTypeCode, models.AccessModeWrite)
if err != nil {
return nil, err
}
}
return &api.Branch{
Name: b.Name,
Commit: ToPayloadCommit(repo, c),
Protected: false,
RequiredApprovals: 0,
EnableStatusCheck: false,
StatusCheckContexts: []string{},
UserCanPush: hasPerm,
UserCanMerge: hasPerm,
}, nil
}
2020-03-19 23:39:08 +08:00
branch := &api.Branch{
Name: b.Name,
Commit: ToPayloadCommit(repo, c),
Protected: true,
RequiredApprovals: bp.RequiredApprovals,
EnableStatusCheck: bp.EnableStatusCheck,
StatusCheckContexts: bp.StatusCheckContexts,
}
if isRepoAdmin {
branch.EffectiveBranchProtectionName = bp.BranchName
}
2020-03-19 23:39:08 +08:00
if user != nil {
permission, err := models.GetUserRepoPermission(repo, user)
if err != nil {
return nil, err
}
2020-03-19 23:39:08 +08:00
branch.UserCanPush = bp.CanUserPush(user.ID)
branch.UserCanMerge = bp.IsUserMergeWhitelisted(user.ID, permission)
2020-03-19 23:39:08 +08:00
}
return branch, nil
}
// ToBranchProtection convert a ProtectedBranch to api.BranchProtection
func ToBranchProtection(bp *models.ProtectedBranch) *api.BranchProtection {
pushWhitelistUsernames, err := models.GetUserNamesByIDs(bp.WhitelistUserIDs)
if err != nil {
log.Error("GetUserNamesByIDs (WhitelistUserIDs): %v", err)
}
mergeWhitelistUsernames, err := models.GetUserNamesByIDs(bp.MergeWhitelistUserIDs)
if err != nil {
log.Error("GetUserNamesByIDs (MergeWhitelistUserIDs): %v", err)
}
approvalsWhitelistUsernames, err := models.GetUserNamesByIDs(bp.ApprovalsWhitelistUserIDs)
if err != nil {
log.Error("GetUserNamesByIDs (ApprovalsWhitelistUserIDs): %v", err)
}
pushWhitelistTeams, err := models.GetTeamNamesByID(bp.WhitelistTeamIDs)
if err != nil {
log.Error("GetTeamNamesByID (WhitelistTeamIDs): %v", err)
}
mergeWhitelistTeams, err := models.GetTeamNamesByID(bp.MergeWhitelistTeamIDs)
if err != nil {
log.Error("GetTeamNamesByID (MergeWhitelistTeamIDs): %v", err)
}
approvalsWhitelistTeams, err := models.GetTeamNamesByID(bp.ApprovalsWhitelistTeamIDs)
if err != nil {
log.Error("GetTeamNamesByID (ApprovalsWhitelistTeamIDs): %v", err)
}
return &api.BranchProtection{
BranchName: bp.BranchName,
EnablePush: bp.CanPush,
EnablePushWhitelist: bp.EnableWhitelist,
PushWhitelistUsernames: pushWhitelistUsernames,
PushWhitelistTeams: pushWhitelistTeams,
PushWhitelistDeployKeys: bp.WhitelistDeployKeys,
EnableMergeWhitelist: bp.EnableMergeWhitelist,
MergeWhitelistUsernames: mergeWhitelistUsernames,
MergeWhitelistTeams: mergeWhitelistTeams,
EnableStatusCheck: bp.EnableStatusCheck,
StatusCheckContexts: bp.StatusCheckContexts,
RequiredApprovals: bp.RequiredApprovals,
EnableApprovalsWhitelist: bp.EnableApprovalsWhitelist,
ApprovalsWhitelistUsernames: approvalsWhitelistUsernames,
ApprovalsWhitelistTeams: approvalsWhitelistTeams,
BlockOnRejectedReviews: bp.BlockOnRejectedReviews,
BlockOnOfficialReviewRequests: bp.BlockOnOfficialReviewRequests,
BlockOnOutdatedBranch: bp.BlockOnOutdatedBranch,
DismissStaleApprovals: bp.DismissStaleApprovals,
RequireSignedCommits: bp.RequireSignedCommits,
ProtectedFilePatterns: bp.ProtectedFilePatterns,
Created: bp.CreatedUnix.AsTime(),
Updated: bp.UpdatedUnix.AsTime(),
}
2016-01-29 03:49:05 +08:00
}
// ToTag convert a git.Tag to an api.Tag
func ToTag(repo *models.Repository, t *git.Tag) *api.Tag {
return &api.Tag{
Name: t.Name,
Message: strings.TrimSpace(t.Message),
ID: t.ID.String(),
Commit: ToCommitMeta(repo, t),
ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"),
TarballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz"),
}
}
// ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification
func ToVerification(c *git.Commit) *api.PayloadCommitVerification {
verif := models.ParseCommitWithSignature(c)
Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631) This PR fixes #7598 by providing a configurable way of signing commits across the Gitea instance. Per repository configurability and import/generation of trusted secure keys is not provided by this PR - from a security PoV that's probably impossible to do properly. Similarly web-signing, that is asking the user to sign something, is not implemented - this could be done at a later stage however. ## Features - [x] If commit.gpgsign is set in .gitconfig sign commits and files created through repofiles. (merges should already have been signed.) - [x] Verify commits signed with the default gpg as valid - [x] Signer, Committer and Author can all be different - [x] Allow signer to be arbitrarily different - We still require the key to have an activated email on Gitea. A more complete implementation would be to use a keyserver and mark external-or-unactivated with an "unknown" trust level icon. - [x] Add a signing-key.gpg endpoint to get the default gpg pub key if available - Rather than add a fake web-flow user I've added this as an endpoint on /api/v1/signing-key.gpg - [x] Try to match the default key with a user on gitea - this is done at verification time - [x] Make things configurable? - app.ini configuration done - [x] when checking commits are signed need to check if they're actually verifiable too - [x] Add documentation I have decided that adjusting the docker to create a default gpg key is not the correct thing to do and therefore have not implemented this.
2019-10-16 21:42:42 +08:00
commitVerification := &api.PayloadCommitVerification{
Verified: verif.Verified,
Reason: verif.Reason,
}
if c.Signature != nil {
Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631) This PR fixes #7598 by providing a configurable way of signing commits across the Gitea instance. Per repository configurability and import/generation of trusted secure keys is not provided by this PR - from a security PoV that's probably impossible to do properly. Similarly web-signing, that is asking the user to sign something, is not implemented - this could be done at a later stage however. ## Features - [x] If commit.gpgsign is set in .gitconfig sign commits and files created through repofiles. (merges should already have been signed.) - [x] Verify commits signed with the default gpg as valid - [x] Signer, Committer and Author can all be different - [x] Allow signer to be arbitrarily different - We still require the key to have an activated email on Gitea. A more complete implementation would be to use a keyserver and mark external-or-unactivated with an "unknown" trust level icon. - [x] Add a signing-key.gpg endpoint to get the default gpg pub key if available - Rather than add a fake web-flow user I've added this as an endpoint on /api/v1/signing-key.gpg - [x] Try to match the default key with a user on gitea - this is done at verification time - [x] Make things configurable? - app.ini configuration done - [x] when checking commits are signed need to check if they're actually verifiable too - [x] Add documentation I have decided that adjusting the docker to create a default gpg key is not the correct thing to do and therefore have not implemented this.
2019-10-16 21:42:42 +08:00
commitVerification.Signature = c.Signature.Signature
commitVerification.Payload = c.Signature.Payload
}
Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631) This PR fixes #7598 by providing a configurable way of signing commits across the Gitea instance. Per repository configurability and import/generation of trusted secure keys is not provided by this PR - from a security PoV that's probably impossible to do properly. Similarly web-signing, that is asking the user to sign something, is not implemented - this could be done at a later stage however. ## Features - [x] If commit.gpgsign is set in .gitconfig sign commits and files created through repofiles. (merges should already have been signed.) - [x] Verify commits signed with the default gpg as valid - [x] Signer, Committer and Author can all be different - [x] Allow signer to be arbitrarily different - We still require the key to have an activated email on Gitea. A more complete implementation would be to use a keyserver and mark external-or-unactivated with an "unknown" trust level icon. - [x] Add a signing-key.gpg endpoint to get the default gpg pub key if available - Rather than add a fake web-flow user I've added this as an endpoint on /api/v1/signing-key.gpg - [x] Try to match the default key with a user on gitea - this is done at verification time - [x] Make things configurable? - app.ini configuration done - [x] when checking commits are signed need to check if they're actually verifiable too - [x] Add documentation I have decided that adjusting the docker to create a default gpg key is not the correct thing to do and therefore have not implemented this.
2019-10-16 21:42:42 +08:00
if verif.SigningUser != nil {
commitVerification.Signer = &structs.PayloadUser{
Name: verif.SigningUser.Name,
Email: verif.SigningUser.Email,
}
2016-01-29 03:49:05 +08:00
}
Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631) This PR fixes #7598 by providing a configurable way of signing commits across the Gitea instance. Per repository configurability and import/generation of trusted secure keys is not provided by this PR - from a security PoV that's probably impossible to do properly. Similarly web-signing, that is asking the user to sign something, is not implemented - this could be done at a later stage however. ## Features - [x] If commit.gpgsign is set in .gitconfig sign commits and files created through repofiles. (merges should already have been signed.) - [x] Verify commits signed with the default gpg as valid - [x] Signer, Committer and Author can all be different - [x] Allow signer to be arbitrarily different - We still require the key to have an activated email on Gitea. A more complete implementation would be to use a keyserver and mark external-or-unactivated with an "unknown" trust level icon. - [x] Add a signing-key.gpg endpoint to get the default gpg pub key if available - Rather than add a fake web-flow user I've added this as an endpoint on /api/v1/signing-key.gpg - [x] Try to match the default key with a user on gitea - this is done at verification time - [x] Make things configurable? - app.ini configuration done - [x] when checking commits are signed need to check if they're actually verifiable too - [x] Add documentation I have decided that adjusting the docker to create a default gpg key is not the correct thing to do and therefore have not implemented this.
2019-10-16 21:42:42 +08:00
return commitVerification
2016-01-29 03:49:05 +08:00
}
2016-11-24 15:04:31 +08:00
// ToPublicKey convert models.PublicKey to api.PublicKey
func ToPublicKey(apiLink string, key *models.PublicKey) *api.PublicKey {
return &api.PublicKey{
ID: key.ID,
Key: key.Content,
URL: fmt.Sprintf("%s%d", apiLink, key.ID),
Title: key.Name,
Fingerprint: key.Fingerprint,
Created: key.CreatedUnix.AsTime(),
}
}
// ToGPGKey converts models.GPGKey to api.GPGKey
func ToGPGKey(key *models.GPGKey) *api.GPGKey {
subkeys := make([]*api.GPGKey, len(key.SubsKey))
for id, k := range key.SubsKey {
subkeys[id] = &api.GPGKey{
ID: k.ID,
PrimaryKeyID: k.PrimaryKeyID,
KeyID: k.KeyID,
PublicKey: k.Content,
Created: k.CreatedUnix.AsTime(),
Expires: k.ExpiredUnix.AsTime(),
CanSign: k.CanSign,
CanEncryptComms: k.CanEncryptComms,
CanEncryptStorage: k.CanEncryptStorage,
CanCertify: k.CanSign,
}
}
emails := make([]*api.GPGKeyEmail, len(key.Emails))
for i, e := range key.Emails {
emails[i] = ToGPGKeyEmail(e)
}
return &api.GPGKey{
ID: key.ID,
PrimaryKeyID: key.PrimaryKeyID,
KeyID: key.KeyID,
PublicKey: key.Content,
Created: key.CreatedUnix.AsTime(),
Expires: key.ExpiredUnix.AsTime(),
Emails: emails,
SubsKey: subkeys,
CanSign: key.CanSign,
CanEncryptComms: key.CanEncryptComms,
CanEncryptStorage: key.CanEncryptStorage,
CanCertify: key.CanSign,
}
}
// ToGPGKeyEmail convert models.EmailAddress to api.GPGKeyEmail
func ToGPGKeyEmail(email *models.EmailAddress) *api.GPGKeyEmail {
return &api.GPGKeyEmail{
Email: email.Email,
Verified: email.IsActivated,
}
}
2016-11-24 15:04:31 +08:00
// ToHook convert models.Webhook to api.Hook
func ToHook(repoLink string, w *models.Webhook) *api.Hook {
config := map[string]string{
"url": w.URL,
"content_type": w.ContentType.Name(),
}
if w.Type == models.SLACK {
s := webhook.GetSlackHook(w)
config["channel"] = s.Channel
config["username"] = s.Username
config["icon_url"] = s.IconURL
config["color"] = s.Color
}
return &api.Hook{
ID: w.ID,
Type: string(w.Type),
URL: fmt.Sprintf("%s/settings/hooks/%d", repoLink, w.ID),
Active: w.IsActive,
Config: config,
Events: w.EventsArray(),
Updated: w.UpdatedUnix.AsTime(),
Created: w.CreatedUnix.AsTime(),
}
}
// ToGitHook convert git.Hook to api.GitHook
func ToGitHook(h *git.Hook) *api.GitHook {
return &api.GitHook{
Name: h.Name(),
IsActive: h.IsActive,
Content: h.Content,
}
}
2016-11-24 15:04:31 +08:00
// ToDeployKey convert models.DeployKey to api.DeployKey
func ToDeployKey(apiLink string, key *models.DeployKey) *api.DeployKey {
return &api.DeployKey{
ID: key.ID,
KeyID: key.KeyID,
Key: key.Content,
Fingerprint: key.Fingerprint,
URL: fmt.Sprintf("%s%d", apiLink, key.ID),
Title: key.Name,
Created: key.CreatedUnix.AsTime(),
ReadOnly: key.Mode == models.AccessModeRead, // All deploy keys are read-only.
}
}
2015-12-17 15:28:47 +08:00
2016-11-24 15:04:31 +08:00
// ToOrganization convert models.User to api.Organization
func ToOrganization(org *models.User) *api.Organization {
2015-12-17 15:28:47 +08:00
return &api.Organization{
ID: org.ID,
AvatarURL: org.AvatarLink(),
UserName: org.Name,
FullName: org.FullName,
Description: org.Description,
Website: org.Website,
Location: org.Location,
Visibility: org.Visibility.String(),
RepoAdminChangeTeamAccess: org.RepoAdminChangeTeamAccess,
2015-12-17 15:28:47 +08:00
}
}
2016-11-24 15:04:31 +08:00
// ToTeam convert models.Team to api.Team
func ToTeam(team *models.Team) *api.Team {
if team == nil {
return nil
}
return &api.Team{
ID: team.ID,
Name: team.Name,
Description: team.Description,
IncludesAllRepositories: team.IncludesAllRepositories,
CanCreateOrgRepo: team.CanCreateOrgRepo,
Permission: team.Authorize.String(),
Units: team.GetUnitNames(),
}
}
// ToAnnotatedTag convert git.Tag to api.AnnotatedTag
func ToAnnotatedTag(repo *models.Repository, t *git.Tag, c *git.Commit) *api.AnnotatedTag {
return &api.AnnotatedTag{
Tag: t.Name,
SHA: t.ID.String(),
Object: ToAnnotatedTagObject(repo, c),
Message: t.Message,
URL: util.URLJoin(repo.APIURL(), "git/tags", t.ID.String()),
Tagger: ToCommitUser(t.Tagger),
Verification: ToVerification(c),
}
}
// ToAnnotatedTagObject convert a git.Commit to an api.AnnotatedTagObject
func ToAnnotatedTagObject(repo *models.Repository, commit *git.Commit) *api.AnnotatedTagObject {
return &api.AnnotatedTagObject{
SHA: commit.ID.String(),
Type: string(git.ObjectCommit),
URL: util.URLJoin(repo.APIURL(), "git/commits", commit.ID.String()),
}
}
Add API endpoint for accessing repo topics (#7963) * Create API endpoints for repo topics. Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Generate swagger Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Add documentation to functions Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Grammar fix Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Fix function comment Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Can't use FindTopics when looking for a single repo topic, as it doesnt use exact match Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Add PUT ​/repos​/{owner}​/{repo}​/topics and remove GET ​/repos​/{owner}​/{repo}​/topics * Ignore if topic is sent twice in same request, refactoring. Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Fix topic dropdown with api changes. Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Style fix Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Update API documentation Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Better way to handle duplicate topics in slice Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Make response element TopicName an array of strings, instead of using an array of TopicName Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Add test cases for API Repo Topics. Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Fix format of tests Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Fix comments Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Fix unit tests after adding some more topics to the test fixture. Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Update models/topic.go Limit multiple if else if ... Co-Authored-By: Antoine GIRARD <sapk@users.noreply.github.com> * Engine as first parameter in function Co-Authored-By: Antoine GIRARD <sapk@users.noreply.github.com> * Replace magic numbers with http status code constants. Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Fix variable scope Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Test one read with login and one with token Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Add some more tests Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Apply suggestions from code review Use empty struct for efficiency Co-Authored-By: Lauris BH <lauris@nix.lv> * Add test case to check access for user with write access Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Fix access, repo admin required to change topics Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Correct first test to be without token Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Any repo reader should be able to access topics. * No need for string pointer Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
2019-09-03 23:46:24 +08:00
// ToTopicResponse convert from models.Topic to api.TopicResponse
func ToTopicResponse(topic *models.Topic) *api.TopicResponse {
return &api.TopicResponse{
ID: topic.ID,
Name: topic.Name,
RepoCount: topic.RepoCount,
Created: topic.CreatedUnix.AsTime(),
Updated: topic.UpdatedUnix.AsTime(),
}
}
// ToOAuth2Application convert from models.OAuth2Application to api.OAuth2Application
func ToOAuth2Application(app *models.OAuth2Application) *api.OAuth2Application {
return &api.OAuth2Application{
ID: app.ID,
Name: app.Name,
ClientID: app.ClientID,
ClientSecret: app.ClientSecret,
RedirectURIs: app.RedirectURIs,
Created: app.CreatedUnix.AsTime(),
}
}
// ToLFSLock convert a LFSLock to api.LFSLock
func ToLFSLock(l *models.LFSLock) *api.LFSLock {
return &api.LFSLock{
ID: strconv.FormatInt(l.ID, 10),
Path: l.Path,
LockedAt: l.Created.Round(time.Second),
Owner: &api.LFSLockOwner{
Name: l.Owner.DisplayName(),
},
}
}