[SHARED] make confirmation clearer for dangerous actions
[GITEA] Fix cancelled migration deletion modal - https://codeberg.org/forgejo/forgejo/pulls/1473 made that dangerous actions such as deletion also would need to type in the owner's name. This was apparently not reflected to the deletion modal for migrations that failed or were cancelled. (cherry picked from commitc38dbd6f88
) (cherry picked from commit7c07592d01
) (cherry picked from commit78637af2b6
) [SHARED] make confirmation clearer for dangerous actions - Currently the confirmation for dangerous actions such as transferring the repository or deleting it only requires the user to ~~copy paste~~ type the repository name. - This can be problematic when the user has a fork or another repository with the same name as an organization's repository, and the confirmation doesn't make clear that it could be deleting the wrong repository. While it's mentioned in the dialog, it's better to be on the safe side and also add the owner's name to be an element that has to be typed for these dangerous actions. - Added integration tests. (cherry picked from commitbf679b24dd
) (cherry picked from commit1963085dd9
) (cherry picked from commitfb94095d19
) (cherry picked from commite1d1e46afe
) (cherry picked from commit93993029e4
) (cherry picked from commitdf3b058179
) (cherry picked from commit8ccc6b9cba
) (cherry picked from commit9fbe28fca3
) (cherry picked from commit4ef2be6dc7
) https://codeberg.org/forgejo/forgejo/pulls/1873 Moved test from repo_test.go to forgejo_confirmation_repo_test.go to avoid conflicts. (cherry picked from commit83cae67aa3
) (cherry picked from commit447009ff56
) (cherry picked from commit72c0a6150a
) (cherry picked from commit8ee9c070b9
)
This commit is contained in:
parent
f69fc23d4b
commit
89aba06403
5 changed files with 164 additions and 12 deletions
|
@ -681,7 +681,7 @@ func SettingsPost(ctx *context.Context) {
|
||||||
ctx.Error(http.StatusNotFound)
|
ctx.Error(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if repo.Name != form.RepoName {
|
if repo.FullName() != form.RepoName {
|
||||||
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
|
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -712,7 +712,7 @@ func SettingsPost(ctx *context.Context) {
|
||||||
ctx.ServerError("Convert Fork", err)
|
ctx.ServerError("Convert Fork", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if repo.Name != form.RepoName {
|
if repo.FullName() != form.RepoName {
|
||||||
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
|
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -745,7 +745,7 @@ func SettingsPost(ctx *context.Context) {
|
||||||
ctx.Error(http.StatusNotFound)
|
ctx.Error(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if repo.Name != form.RepoName {
|
if repo.FullName() != form.RepoName {
|
||||||
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
|
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -827,7 +827,7 @@ func SettingsPost(ctx *context.Context) {
|
||||||
ctx.Error(http.StatusNotFound)
|
ctx.Error(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if repo.Name != form.RepoName {
|
if repo.FullName() != form.RepoName {
|
||||||
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
|
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -851,7 +851,7 @@ func SettingsPost(ctx *context.Context) {
|
||||||
ctx.Error(http.StatusNotFound)
|
ctx.Error(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if repo.Name != form.RepoName {
|
if repo.FullName() != form.RepoName {
|
||||||
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
|
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>
|
<label>
|
||||||
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
||||||
<span class="text red">{{.Repository.Name}}</span>
|
<span class="text red">{{.Repository.FullName}}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="required field">
|
<div class="required field">
|
||||||
|
|
|
@ -816,7 +816,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>
|
<label>
|
||||||
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
||||||
<span class="text red">{{.Repository.Name}}</span>
|
<span class="text red">{{.Repository.FullName}}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="required field">
|
<div class="required field">
|
||||||
|
@ -847,7 +847,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>
|
<label>
|
||||||
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
||||||
<span class="text red">{{.Repository.Name}}</span>
|
<span class="text red">{{.Repository.FullName}}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="required field">
|
<div class="required field">
|
||||||
|
@ -879,7 +879,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>
|
<label>
|
||||||
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
||||||
<span class="text red">{{.Repository.Name}}</span>
|
<span class="text red">{{.Repository.FullName}}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="required field">
|
<div class="required field">
|
||||||
|
@ -917,7 +917,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>
|
<label>
|
||||||
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
||||||
<span class="text red">{{.Repository.Name}}</span>
|
<span class="text red">{{.Repository.FullName}}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="required field">
|
<div class="required field">
|
||||||
|
@ -949,7 +949,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>
|
<label>
|
||||||
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
{{ctx.Locale.Tr "repo.settings.transfer_form_title"}}
|
||||||
<span class="text red">{{.Repository.Name}}</span>
|
<span class="text red">{{.Repository.FullName}}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="required field">
|
<div class="required field">
|
||||||
|
|
151
tests/integration/forgejo_confirmation_repo_test.go
Normal file
151
tests/integration/forgejo_confirmation_repo_test.go
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
gitea_context "code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/translation"
|
||||||
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDangerZoneConfirmation(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
mustInvalidRepoName := func(resp *httptest.ResponseRecorder) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
assert.Contains(t,
|
||||||
|
htmlDoc.doc.Find(".ui.negative.message").Text(),
|
||||||
|
translation.NewLocale("en-US").Tr("form.enterred_invalid_repo_name"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Transfer ownership", func(t *testing.T) {
|
||||||
|
session := loginUser(t, "user2")
|
||||||
|
|
||||||
|
t.Run("Fail", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings", map[string]string{
|
||||||
|
"_csrf": GetCSRF(t, session, "/user2/repo1/settings"),
|
||||||
|
"action": "transfer",
|
||||||
|
"repo_name": "repo1",
|
||||||
|
"new_owner_name": "user1",
|
||||||
|
})
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
mustInvalidRepoName(resp)
|
||||||
|
})
|
||||||
|
t.Run("Pass", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings", map[string]string{
|
||||||
|
"_csrf": GetCSRF(t, session, "/user2/repo1/settings"),
|
||||||
|
"action": "transfer",
|
||||||
|
"repo_name": "user2/repo1",
|
||||||
|
"new_owner_name": "user1",
|
||||||
|
})
|
||||||
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
|
||||||
|
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
|
||||||
|
assert.NotNil(t, flashCookie)
|
||||||
|
assert.EqualValues(t, flashCookie.Value, "success%3DThis%2Brepository%2Bhas%2Bbeen%2Bmarked%2Bfor%2Btransfer%2Band%2Bawaits%2Bconfirmation%2Bfrom%2B%2522User%2BOne%2522")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Convert fork", func(t *testing.T) {
|
||||||
|
session := loginUser(t, "user20")
|
||||||
|
|
||||||
|
t.Run("Fail", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequestWithValues(t, "POST", "/user20/big_test_public_fork_7/settings", map[string]string{
|
||||||
|
"_csrf": GetCSRF(t, session, "/user20/big_test_public_fork_7/settings"),
|
||||||
|
"action": "convert_fork",
|
||||||
|
"repo_name": "big_test_public_fork_7",
|
||||||
|
})
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
mustInvalidRepoName(resp)
|
||||||
|
})
|
||||||
|
t.Run("Pass", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequestWithValues(t, "POST", "/user20/big_test_public_fork_7/settings", map[string]string{
|
||||||
|
"_csrf": GetCSRF(t, session, "/user20/big_test_public_fork_7/settings"),
|
||||||
|
"action": "convert_fork",
|
||||||
|
"repo_name": "user20/big_test_public_fork_7",
|
||||||
|
})
|
||||||
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
|
||||||
|
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
|
||||||
|
assert.NotNil(t, flashCookie)
|
||||||
|
assert.EqualValues(t, flashCookie.Value, "success%3DThe%2Bfork%2Bhas%2Bbeen%2Bconverted%2Binto%2Ba%2Bregular%2Brepository.")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Delete wiki", func(t *testing.T) {
|
||||||
|
session := loginUser(t, "user2")
|
||||||
|
|
||||||
|
t.Run("Fail", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings", map[string]string{
|
||||||
|
"_csrf": GetCSRF(t, session, "/user2/repo1/settings"),
|
||||||
|
"action": "delete-wiki",
|
||||||
|
"repo_name": "repo1",
|
||||||
|
})
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
mustInvalidRepoName(resp)
|
||||||
|
})
|
||||||
|
t.Run("Pass", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings", map[string]string{
|
||||||
|
"_csrf": GetCSRF(t, session, "/user2/repo1/settings"),
|
||||||
|
"action": "delete-wiki",
|
||||||
|
"repo_name": "user2/repo1",
|
||||||
|
})
|
||||||
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
|
||||||
|
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
|
||||||
|
assert.NotNil(t, flashCookie)
|
||||||
|
assert.EqualValues(t, flashCookie.Value, "success%3DThe%2Brepository%2Bwiki%2Bdata%2Bhas%2Bbeen%2Bdeleted.")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
session := loginUser(t, "user2")
|
||||||
|
|
||||||
|
t.Run("Fail", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings", map[string]string{
|
||||||
|
"_csrf": GetCSRF(t, session, "/user2/repo1/settings"),
|
||||||
|
"action": "delete",
|
||||||
|
"repo_name": "repo1",
|
||||||
|
})
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
mustInvalidRepoName(resp)
|
||||||
|
})
|
||||||
|
t.Run("Pass", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequestWithValues(t, "POST", "/user2/repo1/settings", map[string]string{
|
||||||
|
"_csrf": GetCSRF(t, session, "/user2/repo1/settings"),
|
||||||
|
"action": "delete",
|
||||||
|
"repo_name": "user2/repo1",
|
||||||
|
})
|
||||||
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
|
||||||
|
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
|
||||||
|
assert.NotNil(t, flashCookie)
|
||||||
|
assert.EqualValues(t, flashCookie.Value, "success%3DThe%2Brepository%2Bhas%2Bbeen%2Bdeleted.")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -129,7 +130,7 @@ func testDeleteRepository(t *testing.T, session *TestSession, ownerName, repoNam
|
||||||
|
|
||||||
req = NewRequestWithValues(t, "POST", relURL+"?action=delete", map[string]string{
|
req = NewRequestWithValues(t, "POST", relURL+"?action=delete", map[string]string{
|
||||||
"_csrf": htmlDoc.GetCSRF(),
|
"_csrf": htmlDoc.GetCSRF(),
|
||||||
"repo_name": repoName,
|
"repo_name": fmt.Sprintf("%s/%s", ownerName, repoName),
|
||||||
})
|
})
|
||||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue