From 9d4430829823c8f1986828a14343460178edc3da Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Fri, 8 Dec 2017 11:20:15 +0200 Subject: [PATCH 01/16] Add is_writable checkbox to deploy keys interface --- templates/repo/settings/deploy_keys.tmpl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl index 3b22be532b926..aff89efbb998a 100644 --- a/templates/repo/settings/deploy_keys.tmpl +++ b/templates/repo/settings/deploy_keys.tmpl @@ -60,6 +60,15 @@ +
+
+ + + {{$.i18n.Tr "settings.is_writable_info"}} +
+
From 0bb3228a6720b2a9f7938bbe5daa605b67153e4c Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Fri, 8 Dec 2017 13:26:05 +0200 Subject: [PATCH 02/16] Add writable key option to deploy key form --- options/locale/locale_en-US.ini | 2 ++ templates/repo/settings/deploy_keys.tmpl | 18 +++++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 4344a657744c6..b94c24aac9e6a 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -998,6 +998,8 @@ settings.deploy_key_desc = Deploy keys have read-only access. They are not the s settings.no_deploy_keys = You haven't added any deploy keys. settings.title = Title settings.deploy_key_content = Content +settings.is_writable = Allow write access +settings.is_writable_info = Can this key be used to push to this repository? Deploy keys always have pull access. settings.key_been_used = Deploy key content has been used. settings.key_name_used = Deploy key with the same name already exists. settings.add_key_success = New deploy key '%s' has been added successfully! diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl index aff89efbb998a..022d45e609993 100644 --- a/templates/repo/settings/deploy_keys.tmpl +++ b/templates/repo/settings/deploy_keys.tmpl @@ -60,15 +60,15 @@ -
-
- - - {{$.i18n.Tr "settings.is_writable_info"}} -
-
+
+
+ + + {{$.i18n.Tr "repo.settings.is_writable_info" | Str2html}} +
+
From 147063e65eb356563e1f7da46255814f26513f17 Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Fri, 15 Dec 2017 13:35:31 +0200 Subject: [PATCH 03/16] Add support for writable ssh keys in the interface --- models/ssh_key.go | 18 ++++++++++++++---- modules/auth/user_form.go | 7 ++++--- options/locale/locale_en-US.ini | 2 ++ routers/api/v1/repo/key.go | 2 +- routers/repo/setting.go | 2 +- templates/repo/settings/deploy_keys.tmpl | 4 ++-- 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/models/ssh_key.go b/models/ssh_key.go index 4d276ebeb7e6f..4ff0a75d4ff10 100644 --- a/models/ssh_key.go +++ b/models/ssh_key.go @@ -600,6 +600,9 @@ type DeployKey struct { Fingerprint string Content string `xorm:"-"` + Mode AccessMode + IsWritable bool `xorm:"-"` + CreatedUnix util.TimeStamp `xorm:"created"` UpdatedUnix util.TimeStamp `xorm:"updated"` HasRecentActivity bool `xorm:"-"` @@ -610,6 +613,7 @@ type DeployKey struct { func (key *DeployKey) AfterLoad() { key.HasUsed = key.UpdatedUnix > key.CreatedUnix key.HasRecentActivity = key.UpdatedUnix.AddDuration(7*24*time.Hour) > util.TimeStampNow() + key.IsWritable = key.Mode == AccessModeWrite } // GetContent gets associated public key content. @@ -646,7 +650,7 @@ func checkDeployKey(e Engine, keyID, repoID int64, name string) error { } // addDeployKey adds new key-repo relation. -func addDeployKey(e *xorm.Session, keyID, repoID int64, name, fingerprint string) (*DeployKey, error) { +func addDeployKey(e *xorm.Session, keyID, repoID int64, name, fingerprint string, mode AccessMode) (*DeployKey, error) { if err := checkDeployKey(e, keyID, repoID, name); err != nil { return nil, err } @@ -656,6 +660,7 @@ func addDeployKey(e *xorm.Session, keyID, repoID int64, name, fingerprint string RepoID: repoID, Name: name, Fingerprint: fingerprint, + Mode: mode, } _, err := e.Insert(key) return key, err @@ -670,15 +675,20 @@ func HasDeployKey(keyID, repoID int64) bool { } // AddDeployKey add new deploy key to database and authorized_keys file. -func AddDeployKey(repoID int64, name, content string) (*DeployKey, error) { +func AddDeployKey(repoID int64, name, content string, isWritable bool) (*DeployKey, error) { fingerprint, err := calcFingerprint(content) if err != nil { return nil, err } + accessMode := AccessModeRead + if isWritable { + accessMode = AccessModeWrite + } + pkey := &PublicKey{ Fingerprint: fingerprint, - Mode: AccessModeRead, + Mode: accessMode, Type: KeyTypeDeploy, } has, err := x.Get(pkey) @@ -701,7 +711,7 @@ func AddDeployKey(repoID int64, name, content string) (*DeployKey, error) { } } - key, err := addDeployKey(sess, pkey.ID, repoID, name, pkey.Fingerprint) + key, err := addDeployKey(sess, pkey.ID, repoID, name, pkey.Fingerprint, accessMode) if err != nil { return nil, fmt.Errorf("addDeployKey: %v", err) } diff --git a/modules/auth/user_form.go b/modules/auth/user_form.go index ab0bb1e7dd721..d913822a8d108 100644 --- a/modules/auth/user_form.go +++ b/modules/auth/user_form.go @@ -169,9 +169,10 @@ func (f *AddOpenIDForm) Validate(ctx *macaron.Context, errs binding.Errors) bind // AddKeyForm form for adding SSH/GPG key type AddKeyForm struct { - Type string `binding:"OmitEmpty"` - Title string `binding:"Required;MaxSize(50)"` - Content string `binding:"Required"` + Type string `binding:"OmitEmpty"` + Title string `binding:"Required;MaxSize(50)"` + Content string `binding:"Required"` + IsWritable bool } // Validate validates the fields diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index b94c24aac9e6a..b2bb10901f944 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -995,6 +995,8 @@ settings.add_dingtalk_hook_desc = Add Dingtalk integration to y settings.deploy_keys = Deploy Keys settings.add_deploy_key = Add Deploy Key settings.deploy_key_desc = Deploy keys have read-only access. They are not the same as personal account SSH keys. +settings.is_writable = Allow write access +settings.is_writable_info = Can this key be used to push to this repository? Deploy keys always have pull access. settings.no_deploy_keys = You haven't added any deploy keys. settings.title = Title settings.deploy_key_content = Content diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index 42082c356111e..64f138ae3c6c3 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -160,7 +160,7 @@ func CreateDeployKey(ctx *context.APIContext, form api.CreateKeyOption) { return } - key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content) + key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content, false) if err != nil { HandleAddKeyError(ctx, err) return diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 342451b8ad1ed..ccccc55c5a4dc 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -544,7 +544,7 @@ func DeployKeysPost(ctx *context.Context, form auth.AddKeyForm) { return } - key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content) + key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content, form.IsWritable) if err != nil { ctx.Data["HasError"] = true switch { diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl index 022d45e609993..2c8976490c9f9 100644 --- a/templates/repo/settings/deploy_keys.tmpl +++ b/templates/repo/settings/deploy_keys.tmpl @@ -31,7 +31,7 @@ {{.Fingerprint}}
- {{$.i18n.Tr "settings.add_on"}} {{.CreatedUnix.FormatShort}} {{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} {{.UpdatedUnix.FormatShort}}{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}} + {{$.i18n.Tr "settings.add_on"}} {{.CreatedUnix.FormatShort}} {{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} {{.UpdatedUnix.FormatShort}}{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}} - Read{{if .IsWritable}} / Write {{end}}
@@ -62,7 +62,7 @@
- + From e4315d9e061480de1b8c734cf2cbda972f599b8a Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Fri, 15 Dec 2017 14:59:11 +0200 Subject: [PATCH 04/16] Rename IsWritable to ReadOnly --- models/ssh_key.go | 10 +++++----- routers/api/v1/repo/key.go | 2 +- routers/repo/setting.go | 2 +- templates/repo/settings/deploy_keys.tmpl | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/models/ssh_key.go b/models/ssh_key.go index 4ff0a75d4ff10..cdafb358bf44b 100644 --- a/models/ssh_key.go +++ b/models/ssh_key.go @@ -600,8 +600,8 @@ type DeployKey struct { Fingerprint string Content string `xorm:"-"` - Mode AccessMode - IsWritable bool `xorm:"-"` + Mode AccessMode + ReadOnly bool `xorm:"-"` CreatedUnix util.TimeStamp `xorm:"created"` UpdatedUnix util.TimeStamp `xorm:"updated"` @@ -613,7 +613,7 @@ type DeployKey struct { func (key *DeployKey) AfterLoad() { key.HasUsed = key.UpdatedUnix > key.CreatedUnix key.HasRecentActivity = key.UpdatedUnix.AddDuration(7*24*time.Hour) > util.TimeStampNow() - key.IsWritable = key.Mode == AccessModeWrite + key.ReadOnly = key.Mode == AccessModeRead } // GetContent gets associated public key content. @@ -675,14 +675,14 @@ func HasDeployKey(keyID, repoID int64) bool { } // AddDeployKey add new deploy key to database and authorized_keys file. -func AddDeployKey(repoID int64, name, content string, isWritable bool) (*DeployKey, error) { +func AddDeployKey(repoID int64, name, content string, readOnly bool) (*DeployKey, error) { fingerprint, err := calcFingerprint(content) if err != nil { return nil, err } accessMode := AccessModeRead - if isWritable { + if !readOnly { accessMode = AccessModeWrite } diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index 64f138ae3c6c3..a3586d25522bb 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -160,7 +160,7 @@ func CreateDeployKey(ctx *context.APIContext, form api.CreateKeyOption) { return } - key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content, false) + key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content, form.ReadOnly) if err != nil { HandleAddKeyError(ctx, err) return diff --git a/routers/repo/setting.go b/routers/repo/setting.go index ccccc55c5a4dc..d7a61ba26884b 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -544,7 +544,7 @@ func DeployKeysPost(ctx *context.Context, form auth.AddKeyForm) { return } - key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content, form.IsWritable) + key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content, !form.IsWritable) if err != nil { ctx.Data["HasError"] = true switch { diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl index 2c8976490c9f9..812f0dae39801 100644 --- a/templates/repo/settings/deploy_keys.tmpl +++ b/templates/repo/settings/deploy_keys.tmpl @@ -31,7 +31,7 @@ {{.Fingerprint}}
- {{$.i18n.Tr "settings.add_on"}} {{.CreatedUnix.FormatShort}} {{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} {{.UpdatedUnix.FormatShort}}{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}} - Read{{if .IsWritable}} / Write {{end}} + {{$.i18n.Tr "settings.add_on"}} {{.CreatedUnix.FormatShort}} {{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} {{.UpdatedUnix.FormatShort}}{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}} - Read{{if not .ReadOnly}} / Write {{end}}
From 96e85d73361830631cc1fe3603aec4ddd73dcfb0 Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Mon, 18 Dec 2017 12:47:52 +0200 Subject: [PATCH 05/16] Test: create read-only and read-write deploy keys via api --- integrations/api_keys_test.go | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/integrations/api_keys_test.go b/integrations/api_keys_test.go index 8e2b6d3fbf1f3..57e57ed9b2dea 100644 --- a/integrations/api_keys_test.go +++ b/integrations/api_keys_test.go @@ -5,9 +5,11 @@ package integrations import ( + "fmt" "net/http" "testing" + "code.gitea.io/gitea/models" api "code.gitea.io/sdk/gitea" ) @@ -37,3 +39,54 @@ func TestDeleteDeployKeyNoLogin(t *testing.T) { req := NewRequest(t, "DELETE", "/api/v1/repos/user2/repo1/keys/1") MakeRequest(t, req, http.StatusUnauthorized) } + +func TestCreateReadOnlyDeployKey(t *testing.T) { + prepareTestEnv(t) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{Name: "repo1"}).(*models.Repository) + repoOwner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User) + + session := loginUser(t, repoOwner.Name) + + keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys", repoOwner.Name, repo.Name) + rawKeyBody := api.CreateKeyOption{ + Title: "read-only", + Key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment\n", + } + req := NewRequestWithJSON(t, "POST", keysURL, rawKeyBody) + resp := session.MakeRequest(t, req, http.StatusCreated) + + var newDeployKey api.DeployKey + DecodeJSON(t, resp, &newDeployKey) + models.AssertExistsAndLoadBean(t, &models.DeployKey{ + ID: newDeployKey.ID, + Name: rawKeyBody.Title, + Content: rawKeyBody.Key, + ReadOnly: true, + }) +} + +func TestCreateReadWriteDeployKey(t *testing.T) { + prepareTestEnv(t) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{Name: "repo1"}).(*models.Repository) + repoOwner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User) + + session := loginUser(t, repoOwner.Name) + + keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys", repoOwner.Name, repo.Name) + rawKeyBody := api.CreateKeyOption{ + Title: "read-write", + Key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsufOCrDDlT8DLkodnnJtbq7uGflcPae7euTfM+Laq4So+v4WeSV362Rg0O/+Sje1UthrhN6lQkfRkdWIlCRQEXg+LMqr6RhvDfZquE2Xwqv/itlz7LjbdAUdYoO1iH7rMSmYvQh4WEnC/DAacKGbhdGIM/ZBz0z6tHm7bPgbI9ykEKekTmPwQFP1Qebvf5NYOFMWqQ2sCEAI9dBMVLoojsIpV+KADf+BotiIi8yNfTG2rzmzpxBpW9fYjd1Sy1yd4NSUpoPbEJJYJ1TrjiSWlYOVq9Ar8xW1O87i6gBjL/3zN7ANeoYhaAXupdOS6YL22YOK/yC0tJtXwwdh/eSrh", + ReadOnly: false, + } + req := NewRequestWithJSON(t, "POST", keysURL, rawKeyBody) + resp := session.MakeRequest(t, req, http.StatusCreated) + + var newDeployKey api.DeployKey + DecodeJSON(t, resp, &newDeployKey) + models.AssertExistsAndLoadBean(t, &models.DeployKey{ + ID: newDeployKey.ID, + Name: rawKeyBody.Title, + Content: rawKeyBody.Key, + ReadOnly: false, + }) +} From 8becb7e71db47099e7815fc147f7a9d7a4e465ea Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Thu, 28 Dec 2017 14:49:29 +0200 Subject: [PATCH 06/16] Add DeployKey access mode migration --- models/ssh_key.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/ssh_key.go b/models/ssh_key.go index cdafb358bf44b..6c9940e471191 100644 --- a/models/ssh_key.go +++ b/models/ssh_key.go @@ -600,8 +600,8 @@ type DeployKey struct { Fingerprint string Content string `xorm:"-"` - Mode AccessMode - ReadOnly bool `xorm:"-"` + Mode AccessMode `xorm:"NOT NULL DEFAULT 1"` + ReadOnly bool `xorm:"-"` CreatedUnix util.TimeStamp `xorm:"created"` UpdatedUnix util.TimeStamp `xorm:"updated"` From 1142740661fd8e515d9f5622567e158997341fbe Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Thu, 28 Dec 2017 14:57:13 +0200 Subject: [PATCH 07/16] Update gitea sdk via govendor --- vendor/code.gitea.io/sdk/gitea/repo_key.go | 4 ++++ vendor/vendor.json | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/vendor/code.gitea.io/sdk/gitea/repo_key.go b/vendor/code.gitea.io/sdk/gitea/repo_key.go index 03f626cd6ef2e..f2f1038aa3689 100644 --- a/vendor/code.gitea.io/sdk/gitea/repo_key.go +++ b/vendor/code.gitea.io/sdk/gitea/repo_key.go @@ -46,6 +46,10 @@ type CreateKeyOption struct { // required: true // unique: true Key string `json:"key" binding:"Required"` + // Describe if the key has only read access or read/write + // + // required: false + ReadOnly bool `json:"read_only"` } // CreateDeployKey options when create one deploy key diff --git a/vendor/vendor.json b/vendor/vendor.json index 893110ac719fb..828bdd4e43524 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -9,10 +9,10 @@ "revisionTime": "2017-12-22T02:43:26Z" }, { - "checksumSHA1": "QQ7g7B9+EIzGjO14KCGEs9TNEzM=", + "checksumSHA1": "Qtq0kW+BnpYMOriaoCjMa86WGG8=", "path": "code.gitea.io/sdk/gitea", - "revision": "ec7d3af43b598c1a3f2cb12f633b9625649d8e54", - "revisionTime": "2017-11-28T12:30:39Z" + "revision": "79eee8f12c7fc1cc5b802c5cdc5b494ef3733866", + "revisionTime": "2017-12-20T06:57:50Z" }, { "checksumSHA1": "bOODD4Gbw3GfcuQPU2dI40crxxk=", From 1dc6f3e059f72107482d8a8aadeb6bdfa067243b Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Thu, 28 Dec 2017 14:59:59 +0200 Subject: [PATCH 08/16] Fix deploykey migration --- models/migrations/v54.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/migrations/v54.go b/models/migrations/v54.go index 96c26739c6195..fce02e24294f9 100644 --- a/models/migrations/v54.go +++ b/models/migrations/v54.go @@ -28,7 +28,7 @@ func addPullRequestOptions(x *xorm.Engine) error { return err } - //Updating existing issue units + // Updating existing issue units units := make([]*RepoUnit, 0, 100) if err := sess.Where("`type` = ?", V16UnitTypePRs).Find(&units); err != nil { return fmt.Errorf("Query repo units: %v", err) From 9256cf61abae73387f0691c0d6a2416de2418444 Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Thu, 28 Dec 2017 19:45:14 +0200 Subject: [PATCH 09/16] Add unittests for writable deploy keys --- routers/repo/settings_test.go | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 routers/repo/settings_test.go diff --git a/routers/repo/settings_test.go b/routers/repo/settings_test.go new file mode 100644 index 0000000000000..5cdad4773e766 --- /dev/null +++ b/routers/repo/settings_test.go @@ -0,0 +1,61 @@ +// Copyright 2017 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. + +package repo + +import ( + "net/http" + "testing" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/auth" + "code.gitea.io/gitea/modules/test" + + "github.com/stretchr/testify/assert" +) + +func TestAddReadOnlyDeployKey(t *testing.T) { + models.PrepareTestEnv(t) + + ctx := test.MockContext(t, "user2/repo1/settings/keys") + + test.LoadUser(t, ctx, 2) + test.LoadRepo(t, ctx, 2) + + addKeyForm := auth.AddKeyForm{ + Title: "read-only", + Content: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment\n", + } + DeployKeysPost(ctx, addKeyForm) + assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) + + models.AssertExistsAndLoadBean(t, &models.DeployKey{ + Name: addKeyForm.Title, + Content: addKeyForm.Content, + ReadOnly: true, + }) +} + +func TestAddReadWriteOnlyDeployKey(t *testing.T) { + models.PrepareTestEnv(t) + + ctx := test.MockContext(t, "user2/repo1/settings/keys") + + test.LoadUser(t, ctx, 2) + test.LoadRepo(t, ctx, 2) + + addKeyForm := auth.AddKeyForm{ + Title: "read-write", + Content: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment\n", + IsWritable: true, + } + DeployKeysPost(ctx, addKeyForm) + assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) + + models.AssertExistsAndLoadBean(t, &models.DeployKey{ + Name: addKeyForm.Title, + Content: addKeyForm.Content, + ReadOnly: false, + }) +} From dde53ee83c72a506db5c5f014a88ee33cf0f405f Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Thu, 28 Dec 2017 19:52:25 +0200 Subject: [PATCH 10/16] Move template text to locale --- options/locale/locale_en-US.ini | 2 ++ templates/repo/settings/deploy_keys.tmpl | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index b2bb10901f944..5e46ca1369d9e 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -401,6 +401,8 @@ valid_until = Valid until valid_forever = Valid forever last_used = Last used on no_activity = No recent activity +can_read_info = Read +can_write_info = Read key_state_desc = This key has been used in the last 7 days token_state_desc = This token has been used in the last 7 days show_openid = Show on profile diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl index 812f0dae39801..691dfa1693117 100644 --- a/templates/repo/settings/deploy_keys.tmpl +++ b/templates/repo/settings/deploy_keys.tmpl @@ -31,7 +31,7 @@ {{.Fingerprint}}
- {{$.i18n.Tr "settings.add_on"}} {{.CreatedUnix.FormatShort}} {{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} {{.UpdatedUnix.FormatShort}}{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}} - Read{{if not .ReadOnly}} / Write {{end}} + {{$.i18n.Tr "settings.add_on"}} {{.CreatedUnix.FormatShort}} {{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} {{.UpdatedUnix.FormatShort}}{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}} - {{$.i18n.Tr "settings.can_read_info"}}{{if not .ReadOnly}} / {{$.i18n.Tr "settings.can_write_info"}} {{end}}
From 5ec7d626fdb58c450aa8064ff894e935f30ed9a2 Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Thu, 28 Dec 2017 20:18:40 +0200 Subject: [PATCH 11/16] Remove implicit column update --- models/migrations/v54.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/migrations/v54.go b/models/migrations/v54.go index fce02e24294f9..96c26739c6195 100644 --- a/models/migrations/v54.go +++ b/models/migrations/v54.go @@ -28,7 +28,7 @@ func addPullRequestOptions(x *xorm.Engine) error { return err } - // Updating existing issue units + //Updating existing issue units units := make([]*RepoUnit, 0, 100) if err := sess.Where("`type` = ?", V16UnitTypePRs).Find(&units); err != nil { return fmt.Errorf("Query repo units: %v", err) From 328d9f283cc769eca890dc38abebed73f4ff6508 Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Fri, 29 Dec 2017 08:00:12 +0200 Subject: [PATCH 12/16] Remove duplicate locales --- options/locale/locale_en-US.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 5e46ca1369d9e..46d425c871b52 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1002,8 +1002,6 @@ settings.is_writable_info = Can this key be used to push to thi settings.no_deploy_keys = You haven't added any deploy keys. settings.title = Title settings.deploy_key_content = Content -settings.is_writable = Allow write access -settings.is_writable_info = Can this key be used to push to this repository? Deploy keys always have pull access. settings.key_been_used = Deploy key content has been used. settings.key_name_used = Deploy key with the same name already exists. settings.add_key_success = New deploy key '%s' has been added successfully! From d5bc046ae0b9deadc09d78481bbc50a12e76d25e Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Fri, 29 Dec 2017 09:06:24 +0200 Subject: [PATCH 13/16] Replace ReadOnly field with IsReadOnly method --- integrations/api_keys_test.go | 14 +++++++------- models/ssh_key.go | 9 ++++++--- options/locale/locale_en-US.ini | 2 +- routers/repo/settings_test.go | 12 ++++++------ templates/repo/settings/deploy_keys.tmpl | 2 +- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/integrations/api_keys_test.go b/integrations/api_keys_test.go index 57e57ed9b2dea..04805602764bc 100644 --- a/integrations/api_keys_test.go +++ b/integrations/api_keys_test.go @@ -58,10 +58,10 @@ func TestCreateReadOnlyDeployKey(t *testing.T) { var newDeployKey api.DeployKey DecodeJSON(t, resp, &newDeployKey) models.AssertExistsAndLoadBean(t, &models.DeployKey{ - ID: newDeployKey.ID, - Name: rawKeyBody.Title, - Content: rawKeyBody.Key, - ReadOnly: true, + ID: newDeployKey.ID, + Name: rawKeyBody.Title, + Content: rawKeyBody.Key, + Mode: models.AccessModeRead, }) } @@ -74,9 +74,9 @@ func TestCreateReadWriteDeployKey(t *testing.T) { keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys", repoOwner.Name, repo.Name) rawKeyBody := api.CreateKeyOption{ - Title: "read-write", - Key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsufOCrDDlT8DLkodnnJtbq7uGflcPae7euTfM+Laq4So+v4WeSV362Rg0O/+Sje1UthrhN6lQkfRkdWIlCRQEXg+LMqr6RhvDfZquE2Xwqv/itlz7LjbdAUdYoO1iH7rMSmYvQh4WEnC/DAacKGbhdGIM/ZBz0z6tHm7bPgbI9ykEKekTmPwQFP1Qebvf5NYOFMWqQ2sCEAI9dBMVLoojsIpV+KADf+BotiIi8yNfTG2rzmzpxBpW9fYjd1Sy1yd4NSUpoPbEJJYJ1TrjiSWlYOVq9Ar8xW1O87i6gBjL/3zN7ANeoYhaAXupdOS6YL22YOK/yC0tJtXwwdh/eSrh", - ReadOnly: false, + Title: "read-write", + Key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsufOCrDDlT8DLkodnnJtbq7uGflcPae7euTfM+Laq4So+v4WeSV362Rg0O/+Sje1UthrhN6lQkfRkdWIlCRQEXg+LMqr6RhvDfZquE2Xwqv/itlz7LjbdAUdYoO1iH7rMSmYvQh4WEnC/DAacKGbhdGIM/ZBz0z6tHm7bPgbI9ykEKekTmPwQFP1Qebvf5NYOFMWqQ2sCEAI9dBMVLoojsIpV+KADf+BotiIi8yNfTG2rzmzpxBpW9fYjd1Sy1yd4NSUpoPbEJJYJ1TrjiSWlYOVq9Ar8xW1O87i6gBjL/3zN7ANeoYhaAXupdOS6YL22YOK/yC0tJtXwwdh/eSrh", + Mode: models.AccessModeWrite, } req := NewRequestWithJSON(t, "POST", keysURL, rawKeyBody) resp := session.MakeRequest(t, req, http.StatusCreated) diff --git a/models/ssh_key.go b/models/ssh_key.go index 6c9940e471191..2878177d446cf 100644 --- a/models/ssh_key.go +++ b/models/ssh_key.go @@ -600,8 +600,7 @@ type DeployKey struct { Fingerprint string Content string `xorm:"-"` - Mode AccessMode `xorm:"NOT NULL DEFAULT 1"` - ReadOnly bool `xorm:"-"` + Mode AccessMode `xorm:"NOT NULL DEFAULT 1"` CreatedUnix util.TimeStamp `xorm:"created"` UpdatedUnix util.TimeStamp `xorm:"updated"` @@ -613,7 +612,6 @@ type DeployKey struct { func (key *DeployKey) AfterLoad() { key.HasUsed = key.UpdatedUnix > key.CreatedUnix key.HasRecentActivity = key.UpdatedUnix.AddDuration(7*24*time.Hour) > util.TimeStampNow() - key.ReadOnly = key.Mode == AccessModeRead } // GetContent gets associated public key content. @@ -626,6 +624,11 @@ func (key *DeployKey) GetContent() error { return nil } +// IsReadOnly checks if the key can only be used for read operations +func (key *DeployKey) IsReadOnly() bool { + return key.Mode == AccessModeRead +} + func checkDeployKey(e Engine, keyID, repoID int64, name string) error { // Note: We want error detail, not just true or false here. has, err := e. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 46d425c871b52..a1cb077599e76 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -402,7 +402,7 @@ valid_forever = Valid forever last_used = Last used on no_activity = No recent activity can_read_info = Read -can_write_info = Read +can_write_info = Write key_state_desc = This key has been used in the last 7 days token_state_desc = This token has been used in the last 7 days show_openid = Show on profile diff --git a/routers/repo/settings_test.go b/routers/repo/settings_test.go index 5cdad4773e766..392c05f773a77 100644 --- a/routers/repo/settings_test.go +++ b/routers/repo/settings_test.go @@ -31,9 +31,9 @@ func TestAddReadOnlyDeployKey(t *testing.T) { assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.DeployKey{ - Name: addKeyForm.Title, - Content: addKeyForm.Content, - ReadOnly: true, + Name: addKeyForm.Title, + Content: addKeyForm.Content, + Mode: models.AccessModeRead, }) } @@ -54,8 +54,8 @@ func TestAddReadWriteOnlyDeployKey(t *testing.T) { assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.DeployKey{ - Name: addKeyForm.Title, - Content: addKeyForm.Content, - ReadOnly: false, + Name: addKeyForm.Title, + Content: addKeyForm.Content, + Mode: models.AccessModeWrite, }) } diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl index 691dfa1693117..f19055f2b531d 100644 --- a/templates/repo/settings/deploy_keys.tmpl +++ b/templates/repo/settings/deploy_keys.tmpl @@ -31,7 +31,7 @@ {{.Fingerprint}}
- {{$.i18n.Tr "settings.add_on"}} {{.CreatedUnix.FormatShort}} {{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} {{.UpdatedUnix.FormatShort}}{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}} - {{$.i18n.Tr "settings.can_read_info"}}{{if not .ReadOnly}} / {{$.i18n.Tr "settings.can_write_info"}} {{end}} + {{$.i18n.Tr "settings.add_on"}} {{.CreatedUnix.FormatShort}} {{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} {{.UpdatedUnix.FormatShort}}{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}} - {{$.i18n.Tr "settings.can_read_info"}}{{if not .IsReadOnly}} / {{$.i18n.Tr "settings.can_write_info"}} {{end}}
From af5191c6c39094e7ad94675039d9306ded08adb8 Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Sat, 30 Dec 2017 14:08:16 +0200 Subject: [PATCH 14/16] Fix deploy_keys related integration test --- integrations/api_keys_test.go | 14 +++++++------- models/fixtures/deploy_key.yml | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 models/fixtures/deploy_key.yml diff --git a/integrations/api_keys_test.go b/integrations/api_keys_test.go index 04805602764bc..b2ae1035ce10e 100644 --- a/integrations/api_keys_test.go +++ b/integrations/api_keys_test.go @@ -49,8 +49,9 @@ func TestCreateReadOnlyDeployKey(t *testing.T) { keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys", repoOwner.Name, repo.Name) rawKeyBody := api.CreateKeyOption{ - Title: "read-only", - Key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment\n", + Title: "read-only", + Key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment\n", + ReadOnly: true, } req := NewRequestWithJSON(t, "POST", keysURL, rawKeyBody) resp := session.MakeRequest(t, req, http.StatusCreated) @@ -76,7 +77,6 @@ func TestCreateReadWriteDeployKey(t *testing.T) { rawKeyBody := api.CreateKeyOption{ Title: "read-write", Key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsufOCrDDlT8DLkodnnJtbq7uGflcPae7euTfM+Laq4So+v4WeSV362Rg0O/+Sje1UthrhN6lQkfRkdWIlCRQEXg+LMqr6RhvDfZquE2Xwqv/itlz7LjbdAUdYoO1iH7rMSmYvQh4WEnC/DAacKGbhdGIM/ZBz0z6tHm7bPgbI9ykEKekTmPwQFP1Qebvf5NYOFMWqQ2sCEAI9dBMVLoojsIpV+KADf+BotiIi8yNfTG2rzmzpxBpW9fYjd1Sy1yd4NSUpoPbEJJYJ1TrjiSWlYOVq9Ar8xW1O87i6gBjL/3zN7ANeoYhaAXupdOS6YL22YOK/yC0tJtXwwdh/eSrh", - Mode: models.AccessModeWrite, } req := NewRequestWithJSON(t, "POST", keysURL, rawKeyBody) resp := session.MakeRequest(t, req, http.StatusCreated) @@ -84,9 +84,9 @@ func TestCreateReadWriteDeployKey(t *testing.T) { var newDeployKey api.DeployKey DecodeJSON(t, resp, &newDeployKey) models.AssertExistsAndLoadBean(t, &models.DeployKey{ - ID: newDeployKey.ID, - Name: rawKeyBody.Title, - Content: rawKeyBody.Key, - ReadOnly: false, + ID: newDeployKey.ID, + Name: rawKeyBody.Title, + Content: rawKeyBody.Key, + Mode: models.AccessModeWrite, }) } diff --git a/models/fixtures/deploy_key.yml b/models/fixtures/deploy_key.yml new file mode 100644 index 0000000000000..ca780a73aa0c1 --- /dev/null +++ b/models/fixtures/deploy_key.yml @@ -0,0 +1 @@ +[] # empty From 9337688a212f2f56355a4f4df357ecde39e1aa38 Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Sat, 6 Jan 2018 22:01:41 +0200 Subject: [PATCH 15/16] Rename v54 migration with v55 --- models/migrations/migrations.go | 2 ++ models/migrations/{v54.go => v55.go} | 0 2 files changed, 2 insertions(+) rename models/migrations/{v54.go => v55.go} (100%) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 90f286056f1de..37f3717ff4bde 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -162,6 +162,8 @@ var migrations = []Migration{ NewMigration("add reactions", addReactions), // v54 -> v55 NewMigration("add pull request options", addPullRequestOptions), + // v55 -> v56 + NewMigration("add writable deploy keys", addModeToDeploKeys), } // Migrate database to current version diff --git a/models/migrations/v54.go b/models/migrations/v55.go similarity index 100% rename from models/migrations/v54.go rename to models/migrations/v55.go From b3e094eaa47143615e381fa0b2947c1fb76178e1 Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Sat, 6 Jan 2018 22:55:35 +0200 Subject: [PATCH 16/16] Fix migration hell --- models/migrations/v54.go | 57 ++++++++++++++++++++++++++++++++++++++++ models/migrations/v55.go | 50 ++++++----------------------------- 2 files changed, 65 insertions(+), 42 deletions(-) create mode 100644 models/migrations/v54.go diff --git a/models/migrations/v54.go b/models/migrations/v54.go new file mode 100644 index 0000000000000..96c26739c6195 --- /dev/null +++ b/models/migrations/v54.go @@ -0,0 +1,57 @@ +// Copyright 2017 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. + +package migrations + +import ( + "fmt" + + "code.gitea.io/gitea/modules/util" + + "github.com/go-xorm/xorm" +) + +func addPullRequestOptions(x *xorm.Engine) error { + // RepoUnit describes all units of a repository + type RepoUnit struct { + ID int64 + RepoID int64 `xorm:"INDEX(s)"` + Type int `xorm:"INDEX(s)"` + Config map[string]interface{} `xorm:"JSON"` + CreatedUnix util.TimeStamp `xorm:"INDEX CREATED"` + } + + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + + //Updating existing issue units + units := make([]*RepoUnit, 0, 100) + if err := sess.Where("`type` = ?", V16UnitTypePRs).Find(&units); err != nil { + return fmt.Errorf("Query repo units: %v", err) + } + for _, unit := range units { + if unit.Config == nil { + unit.Config = make(map[string]interface{}) + } + if _, ok := unit.Config["IgnoreWhitespaceConflicts"]; !ok { + unit.Config["IgnoreWhitespaceConflicts"] = false + } + if _, ok := unit.Config["AllowMerge"]; !ok { + unit.Config["AllowMerge"] = true + } + if _, ok := unit.Config["AllowRebase"]; !ok { + unit.Config["AllowRebase"] = true + } + if _, ok := unit.Config["AllowSquash"]; !ok { + unit.Config["AllowSquash"] = true + } + if _, err := sess.ID(unit.ID).Cols("config").Update(unit); err != nil { + return err + } + } + return sess.Commit() +} diff --git a/models/migrations/v55.go b/models/migrations/v55.go index 96c26739c6195..32f4e8ac04c44 100644 --- a/models/migrations/v55.go +++ b/models/migrations/v55.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Gitea 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. @@ -7,51 +7,17 @@ package migrations import ( "fmt" - "code.gitea.io/gitea/modules/util" - + "code.gitea.io/gitea/models" "github.com/go-xorm/xorm" ) -func addPullRequestOptions(x *xorm.Engine) error { - // RepoUnit describes all units of a repository - type RepoUnit struct { - ID int64 - RepoID int64 `xorm:"INDEX(s)"` - Type int `xorm:"INDEX(s)"` - Config map[string]interface{} `xorm:"JSON"` - CreatedUnix util.TimeStamp `xorm:"INDEX CREATED"` - } - - sess := x.NewSession() - defer sess.Close() - if err := sess.Begin(); err != nil { - return err +func addModeToDeploKeys(x *xorm.Engine) error { + type DeployKey struct { + Mode models.AccessMode `xorm:"NOT NULL DEFAULT 1"` } - //Updating existing issue units - units := make([]*RepoUnit, 0, 100) - if err := sess.Where("`type` = ?", V16UnitTypePRs).Find(&units); err != nil { - return fmt.Errorf("Query repo units: %v", err) - } - for _, unit := range units { - if unit.Config == nil { - unit.Config = make(map[string]interface{}) - } - if _, ok := unit.Config["IgnoreWhitespaceConflicts"]; !ok { - unit.Config["IgnoreWhitespaceConflicts"] = false - } - if _, ok := unit.Config["AllowMerge"]; !ok { - unit.Config["AllowMerge"] = true - } - if _, ok := unit.Config["AllowRebase"]; !ok { - unit.Config["AllowRebase"] = true - } - if _, ok := unit.Config["AllowSquash"]; !ok { - unit.Config["AllowSquash"] = true - } - if _, err := sess.ID(unit.ID).Cols("config").Update(unit); err != nil { - return err - } + if err := x.Sync2(new(DeployKey)); err != nil { + return fmt.Errorf("Sync2: %v", err) } - return sess.Commit() + return nil }