diff --git a/modules/setting/security.go b/modules/setting/security.go index 3d12fcf8d9..2f798b75c7 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -13,8 +13,9 @@ import ( "code.gitea.io/gitea/modules/log" ) +// Security settings + var ( - // Security settings InstallLock bool SecretKey string InternalToken string // internal access token @@ -27,7 +28,7 @@ var ( ReverseProxyTrustedProxies []string MinPasswordLength int ImportLocalPaths bool - DisableGitHooks bool + DisableGitHooks = true DisableWebhooks bool OnlyAllowPushIfGiteaEnvironmentSet bool PasswordComplexity []string diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 7529cadca4..48d3a8ff89 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -131,15 +131,9 @@ func NewFuncMap() template.FuncMap { "EnableTimetracking": func() bool { return setting.Service.EnableTimetracking }, - "DisableGitHooks": func() bool { - return setting.DisableGitHooks - }, "DisableWebhooks": func() bool { return setting.DisableWebhooks }, - "DisableImportLocal": func() bool { - return !setting.ImportLocalPaths - }, "UserThemeName": userThemeName, "NotificationSettings": func() map[string]any { return map[string]any{ diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index be2ba4424c..f6a3af1c86 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -313,6 +313,8 @@ func editUserCommon(ctx *context.Context) { ctx.Data["PageIsAdminUsers"] = true ctx.Data["DisableRegularOrgCreation"] = setting.Admin.DisableRegularOrgCreation ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations + ctx.Data["DisableGitHooks"] = setting.DisableGitHooks + ctx.Data["DisableImportLocal"] = !setting.ImportLocalPaths ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice() ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx) } diff --git a/templates/admin/user/edit.tmpl b/templates/admin/user/edit.tmpl index 41b00defb4..d591a645d8 100644 --- a/templates/admin/user/edit.tmpl +++ b/templates/admin/user/edit.tmpl @@ -128,16 +128,16 @@ -
+
- +
-
+
- +
{{if not .DisableRegularOrgCreation}} diff --git a/tests/gitea-repositories-meta/user2/git_hooks_test.git/hooks/pre-receive.d/pre-receive b/tests/gitea-repositories-meta/user2/git_hooks_test.git/hooks/pre-receive.d/pre-receive index b26a3b9b68..205086810d 100755 --- a/tests/gitea-repositories-meta/user2/git_hooks_test.git/hooks/pre-receive.d/pre-receive +++ b/tests/gitea-repositories-meta/user2/git_hooks_test.git/hooks/pre-receive.d/pre-receive @@ -1,3 +1,2 @@ #!/bin/bash - -echo Hello, World! +echo "TestGitHookScript" diff --git a/tests/integration/api_repo_git_hook_test.go b/tests/integration/api_repo_git_hook_test.go index 9917b41790..c28c4336e2 100644 --- a/tests/integration/api_repo_git_hook_test.go +++ b/tests/integration/api_repo_git_hook_test.go @@ -12,185 +12,190 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" ) -const testHookContent = `#!/bin/bash +func TestAPIGitHooks(t *testing.T) { + defer tests.PrepareTestEnv(t)() + defer test.MockVariableValue(&setting.DisableGitHooks, false)() -echo Hello, World! + const testHookContent = `#!/bin/bash +echo "TestGitHookScript" ` -func TestAPIListGitHooks(t *testing.T) { - defer tests.PrepareTestEnv(t)() + t.Run("ListGitHooks", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 37}) - owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 37}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - // user1 is an admin user - session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git", owner.Name, repo.Name). - AddTokenAuth(token) - resp := MakeRequest(t, req, http.StatusOK) - var apiGitHooks []*api.GitHook - DecodeJSON(t, resp, &apiGitHooks) - assert.Len(t, apiGitHooks, 3) - for _, apiGitHook := range apiGitHooks { - if apiGitHook.Name == "pre-receive" { - assert.True(t, apiGitHook.IsActive) - assert.Equal(t, testHookContent, apiGitHook.Content) - } else { + // user1 is an admin user + session := loginUser(t, "user1") + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git", owner.Name, repo.Name). + AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusOK) + var apiGitHooks []*api.GitHook + DecodeJSON(t, resp, &apiGitHooks) + assert.Len(t, apiGitHooks, 3) + for _, apiGitHook := range apiGitHooks { + if apiGitHook.Name == "pre-receive" { + assert.True(t, apiGitHook.IsActive) + assert.Equal(t, testHookContent, apiGitHook.Content) + } else { + assert.False(t, apiGitHook.IsActive) + assert.Empty(t, apiGitHook.Content) + } + } + }) + + t.Run("NoGitHooks", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + // user1 is an admin user + session := loginUser(t, "user1") + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git", owner.Name, repo.Name). + AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusOK) + var apiGitHooks []*api.GitHook + DecodeJSON(t, resp, &apiGitHooks) + assert.Len(t, apiGitHooks, 3) + for _, apiGitHook := range apiGitHooks { assert.False(t, apiGitHook.IsActive) assert.Empty(t, apiGitHook.Content) } - } -} - -func TestAPIListGitHooksNoHooks(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - - // user1 is an admin user - session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git", owner.Name, repo.Name). - AddTokenAuth(token) - resp := MakeRequest(t, req, http.StatusOK) - var apiGitHooks []*api.GitHook - DecodeJSON(t, resp, &apiGitHooks) - assert.Len(t, apiGitHooks, 3) - for _, apiGitHook := range apiGitHooks { - assert.False(t, apiGitHook.IsActive) - assert.Empty(t, apiGitHook.Content) - } -} - -func TestAPIListGitHooksNoAccess(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - - session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git", owner.Name, repo.Name). - AddTokenAuth(token) - MakeRequest(t, req, http.StatusForbidden) -} - -func TestAPIGetGitHook(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 37}) - owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - - // user1 is an admin user - session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name). - AddTokenAuth(token) - resp := MakeRequest(t, req, http.StatusOK) - var apiGitHook *api.GitHook - DecodeJSON(t, resp, &apiGitHook) - assert.True(t, apiGitHook.IsActive) - assert.Equal(t, testHookContent, apiGitHook.Content) -} - -func TestAPIGetGitHookNoAccess(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - - session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name). - AddTokenAuth(token) - MakeRequest(t, req, http.StatusForbidden) -} - -func TestAPIEditGitHook(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - - // user1 is an admin user - session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - - urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive", - owner.Name, repo.Name) - req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditGitHookOption{ - Content: testHookContent, - }).AddTokenAuth(token) - resp := MakeRequest(t, req, http.StatusOK) - var apiGitHook *api.GitHook - DecodeJSON(t, resp, &apiGitHook) - assert.True(t, apiGitHook.IsActive) - assert.Equal(t, testHookContent, apiGitHook.Content) - - req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name). - AddTokenAuth(token) - resp = MakeRequest(t, req, http.StatusOK) - var apiGitHook2 *api.GitHook - DecodeJSON(t, resp, &apiGitHook2) - assert.True(t, apiGitHook2.IsActive) - assert.Equal(t, testHookContent, apiGitHook2.Content) -} - -func TestAPIEditGitHookNoAccess(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - - session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name) - req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditGitHookOption{ - Content: testHookContent, - }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusForbidden) -} - -func TestAPIDeleteGitHook(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 37}) - owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - - // user1 is an admin user - session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - - req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name). - AddTokenAuth(token) - MakeRequest(t, req, http.StatusNoContent) - - req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name). - AddTokenAuth(token) - resp := MakeRequest(t, req, http.StatusOK) - var apiGitHook2 *api.GitHook - DecodeJSON(t, resp, &apiGitHook2) - assert.False(t, apiGitHook2.IsActive) - assert.Empty(t, apiGitHook2.Content) -} - -func TestAPIDeleteGitHookNoAccess(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - - session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name). - AddTokenAuth(token) - MakeRequest(t, req, http.StatusForbidden) + }) + + t.Run("ListGitHooksNoAccess", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + session := loginUser(t, owner.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git", owner.Name, repo.Name). + AddTokenAuth(token) + MakeRequest(t, req, http.StatusForbidden) + }) + + t.Run("GetGitHook", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 37}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + // user1 is an admin user + session := loginUser(t, "user1") + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name). + AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusOK) + var apiGitHook *api.GitHook + DecodeJSON(t, resp, &apiGitHook) + assert.True(t, apiGitHook.IsActive) + assert.Equal(t, testHookContent, apiGitHook.Content) + }) + t.Run("GetGitHookNoAccess", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + session := loginUser(t, owner.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name). + AddTokenAuth(token) + MakeRequest(t, req, http.StatusForbidden) + }) + + t.Run("EditGitHook", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + // user1 is an admin user + session := loginUser(t, "user1") + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + + urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive", + owner.Name, repo.Name) + req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditGitHookOption{ + Content: testHookContent, + }).AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusOK) + var apiGitHook *api.GitHook + DecodeJSON(t, resp, &apiGitHook) + assert.True(t, apiGitHook.IsActive) + assert.Equal(t, testHookContent, apiGitHook.Content) + + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name). + AddTokenAuth(token) + resp = MakeRequest(t, req, http.StatusOK) + var apiGitHook2 *api.GitHook + DecodeJSON(t, resp, &apiGitHook2) + assert.True(t, apiGitHook2.IsActive) + assert.Equal(t, testHookContent, apiGitHook2.Content) + }) + + t.Run("EditGitHookNoAccess", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + session := loginUser(t, owner.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name) + req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditGitHookOption{ + Content: testHookContent, + }).AddTokenAuth(token) + MakeRequest(t, req, http.StatusForbidden) + }) + + t.Run("DeleteGitHook", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 37}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + // user1 is an admin user + session := loginUser(t, "user1") + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + + req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name). + AddTokenAuth(token) + MakeRequest(t, req, http.StatusNoContent) + + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name). + AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusOK) + var apiGitHook2 *api.GitHook + DecodeJSON(t, resp, &apiGitHook2) + assert.False(t, apiGitHook2.IsActive) + assert.Empty(t, apiGitHook2.Content) + }) + + t.Run("DeleteGitHookNoAccess", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + session := loginUser(t, owner.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive", owner.Name, repo.Name). + AddTokenAuth(token) + MakeRequest(t, req, http.StatusForbidden) + }) } diff --git a/tests/mssql.ini.tmpl b/tests/mssql.ini.tmpl index 77c969e813..b50816b2cd 100644 --- a/tests/mssql.ini.tmpl +++ b/tests/mssql.ini.tmpl @@ -93,7 +93,6 @@ COLORIZE = true LEVEL = Debug [security] -DISABLE_GIT_HOOKS = false INSTALL_LOCK = true SECRET_KEY = 9pCviYTWSb INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ diff --git a/tests/mysql.ini.tmpl b/tests/mysql.ini.tmpl index 0fddde46de..ec8307acc3 100644 --- a/tests/mysql.ini.tmpl +++ b/tests/mysql.ini.tmpl @@ -94,7 +94,6 @@ COLORIZE = true LEVEL = Debug [security] -DISABLE_GIT_HOOKS = false INSTALL_LOCK = true SECRET_KEY = 9pCviYTWSb INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ diff --git a/tests/pgsql.ini.tmpl b/tests/pgsql.ini.tmpl index 695662c2e9..139ea9c2b7 100644 --- a/tests/pgsql.ini.tmpl +++ b/tests/pgsql.ini.tmpl @@ -94,7 +94,6 @@ COLORIZE = true LEVEL = Debug [security] -DISABLE_GIT_HOOKS = false INSTALL_LOCK = true SECRET_KEY = 9pCviYTWSb INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ diff --git a/tests/sqlite.ini.tmpl b/tests/sqlite.ini.tmpl index 1cbcd8b2e5..2f7a3e8182 100644 --- a/tests/sqlite.ini.tmpl +++ b/tests/sqlite.ini.tmpl @@ -93,7 +93,6 @@ COLORIZE = true LEVEL = Debug [security] -DISABLE_GIT_HOOKS = false INSTALL_LOCK = true SECRET_KEY = 9pCviYTWSb INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTI3OTU5ODN9.OQkH5UmzID2XBdwQ9TAI6Jj2t1X-wElVTjbE7aoN4I8