mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-30 23:53:52 +03:00
[GITEA] 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 commit64e38b3363
) (cherry picked from commit0c2a78fa48
) (cherry picked from commite8aa66f1dd
) (cherry picked from commit55b5aa0239
)
This commit is contained in:
parent
debf9b1ec9
commit
a448744b7b
4 changed files with 150 additions and 11 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,15 @@ package integration
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
gitea_context "code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/translation"
|
||||||
"code.gitea.io/gitea/tests"
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
@ -448,3 +451,138 @@ func TestGeneratedSourceLink(t *testing.T) {
|
||||||
assert.Equal(t, "/user27/repo49/src/commit/aacbdfe9e1c4b47f60abe81849045fa4e96f1d75/test/test.txt", dataURL)
|
assert.Equal(t, "/user27/repo49/src/commit/aacbdfe9e1c4b47f60abe81849045fa4e96f1d75/test/test.txt", dataURL)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue