mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-01-23 14:43:21 +00:00
Merge pull request 'Optionally allow anyone to edit Wikis' (#2001) from algernon/forgejo:f/world-editable-wiki into forgejo-dependency
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2001 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
This commit is contained in:
commit
cdc909ddd8
10 changed files with 164 additions and 10 deletions
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"code.gitea.io/gitea/models/forgejo/semver"
|
||||
forgejo_v1_20 "code.gitea.io/gitea/models/forgejo_migrations/v1_20"
|
||||
forgejo_v1_22 "code.gitea.io/gitea/models/forgejo_migrations/v1_22"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -43,6 +44,8 @@ var migrations = []*Migration{
|
|||
NewMigration("create the forgejo_sem_ver table", forgejo_v1_20.CreateSemVerTable),
|
||||
// v2 -> v3
|
||||
NewMigration("create the forgejo_auth_token table", forgejo_v1_20.CreateAuthorizationTokenTable),
|
||||
// v3 -> v4
|
||||
NewMigration("Add default_permissions to repo_unit", forgejo_v1_22.AddDefaultPermissionsToRepoUnit),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current Forgejo database version.
|
||||
|
|
17
models/forgejo_migrations/v1_22/v4.go
Normal file
17
models/forgejo_migrations/v1_22/v4.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddDefaultPermissionsToRepoUnit(x *xorm.Engine) error {
|
||||
type RepoUnit struct {
|
||||
ID int64
|
||||
DefaultPermissions int `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
return x.Sync(&RepoUnit{})
|
||||
}
|
|
@ -33,6 +33,16 @@ func (p *Permission) IsAdmin() bool {
|
|||
return p.AccessMode >= perm_model.AccessModeAdmin
|
||||
}
|
||||
|
||||
// IsGloballyWriteable returns true if the unit is writeable by all users of the instance.
|
||||
func (p *Permission) IsGloballyWriteable(unitType unit.Type) bool {
|
||||
for _, u := range p.Units {
|
||||
if u.Type == unitType {
|
||||
return u.DefaultPermissions == repo_model.UnitAccessModeWrite
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasAccess returns true if the current user has at least read access to any unit of this repository
|
||||
func (p *Permission) HasAccess() bool {
|
||||
if p.UnitsMode == nil {
|
||||
|
@ -198,7 +208,19 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
|
|||
if err := repo.LoadOwner(ctx); err != nil {
|
||||
return perm, err
|
||||
}
|
||||
|
||||
if !repo.Owner.IsOrganization() {
|
||||
// for a public repo, different repo units may have different default
|
||||
// permissions for non-restricted users.
|
||||
if !repo.IsPrivate && !user.IsRestricted && len(repo.Units) > 0 {
|
||||
perm.UnitsMode = make(map[unit.Type]perm_model.AccessMode)
|
||||
for _, u := range repo.Units {
|
||||
if _, ok := perm.UnitsMode[u.Type]; !ok {
|
||||
perm.UnitsMode[u.Type] = u.DefaultPermissions.ToAccessMode(perm.AccessMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return perm, nil
|
||||
}
|
||||
|
||||
|
@ -239,10 +261,12 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
|
|||
}
|
||||
}
|
||||
|
||||
// for a public repo on an organization, a non-restricted user has read permission on non-team defined units.
|
||||
// for a public repo on an organization, a non-restricted user should
|
||||
// have the same permission on non-team defined units as the default
|
||||
// permissions for the repo unit.
|
||||
if !found && !repo.IsPrivate && !user.IsRestricted {
|
||||
if _, ok := perm.UnitsMode[u.Type]; !ok {
|
||||
perm.UnitsMode[u.Type] = perm_model.AccessModeRead
|
||||
perm.UnitsMode[u.Type] = u.DefaultPermissions.ToAccessMode(perm_model.AccessModeRead)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -39,13 +40,43 @@ func (err ErrUnitTypeNotExist) Unwrap() error {
|
|||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// RepoUnitAccessMode specifies the users access mode to a repo unit
|
||||
type UnitAccessMode int
|
||||
|
||||
const (
|
||||
// UnitAccessModeUnset - no unit mode set
|
||||
UnitAccessModeUnset UnitAccessMode = iota // 0
|
||||
// UnitAccessModeNone no access
|
||||
UnitAccessModeNone // 1
|
||||
// UnitAccessModeRead read access
|
||||
UnitAccessModeRead // 2
|
||||
// UnitAccessModeWrite write access
|
||||
UnitAccessModeWrite // 3
|
||||
)
|
||||
|
||||
func (mode UnitAccessMode) ToAccessMode(modeIfUnset perm.AccessMode) perm.AccessMode {
|
||||
switch mode {
|
||||
case UnitAccessModeUnset:
|
||||
return modeIfUnset
|
||||
case UnitAccessModeNone:
|
||||
return perm.AccessModeNone
|
||||
case UnitAccessModeRead:
|
||||
return perm.AccessModeRead
|
||||
case UnitAccessModeWrite:
|
||||
return perm.AccessModeWrite
|
||||
default:
|
||||
return perm.AccessModeNone
|
||||
}
|
||||
}
|
||||
|
||||
// RepoUnit describes all units of a repository
|
||||
type RepoUnit struct { //revive:disable-line:exported
|
||||
ID int64
|
||||
RepoID int64 `xorm:"INDEX(s)"`
|
||||
Type unit.Type `xorm:"INDEX(s)"`
|
||||
Config convert.Conversion `xorm:"TEXT"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
|
||||
ID int64
|
||||
RepoID int64 `xorm:"INDEX(s)"`
|
||||
Type unit.Type `xorm:"INDEX(s)"`
|
||||
Config convert.Conversion `xorm:"TEXT"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
|
||||
DefaultPermissions UnitAccessMode `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -6,6 +6,8 @@ package repo
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -28,3 +30,10 @@ func TestActionsConfig(t *testing.T) {
|
|||
cfg.DisableWorkflow("test3.yaml")
|
||||
assert.EqualValues(t, "test1.yaml,test2.yaml,test3.yaml", cfg.ToString())
|
||||
}
|
||||
|
||||
func TestRepoUnitAccessMode(t *testing.T) {
|
||||
assert.Equal(t, UnitAccessModeNone.ToAccessMode(perm.AccessModeAdmin), perm.AccessModeNone)
|
||||
assert.Equal(t, UnitAccessModeRead.ToAccessMode(perm.AccessModeAdmin), perm.AccessModeRead)
|
||||
assert.Equal(t, UnitAccessModeWrite.ToAccessMode(perm.AccessModeAdmin), perm.AccessModeWrite)
|
||||
assert.Equal(t, UnitAccessModeUnset.ToAccessMode(perm.AccessModeRead), perm.AccessModeRead)
|
||||
}
|
||||
|
|
|
@ -2036,6 +2036,7 @@ settings.branches.update_default_branch = Update Default Branch
|
|||
settings.branches.add_new_rule = Add New Rule
|
||||
settings.advanced_settings = Advanced Settings
|
||||
settings.wiki_desc = Enable Repository Wiki
|
||||
settings.wiki_globally_editable = Allow anyone to edit the Wiki
|
||||
settings.use_internal_wiki = Use Built-In Wiki
|
||||
settings.use_external_wiki = Use External Wiki
|
||||
settings.external_wiki_url = External Wiki URL
|
||||
|
|
|
@ -473,10 +473,17 @@ func SettingsPost(ctx *context.Context) {
|
|||
})
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
|
||||
} else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
|
||||
var wikiPermissions repo_model.UnitAccessMode
|
||||
if form.GloballyWriteableWiki {
|
||||
wikiPermissions = repo_model.UnitAccessModeWrite
|
||||
} else {
|
||||
wikiPermissions = repo_model.UnitAccessModeRead
|
||||
}
|
||||
units = append(units, repo_model.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: unit_model.TypeWiki,
|
||||
Config: new(repo_model.UnitConfig),
|
||||
RepoID: repo.ID,
|
||||
Type: unit_model.TypeWiki,
|
||||
Config: new(repo_model.UnitConfig),
|
||||
DefaultPermissions: wikiPermissions,
|
||||
})
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
|
||||
} else {
|
||||
|
|
|
@ -140,6 +140,7 @@ type RepoSettingForm struct {
|
|||
// Advanced settings
|
||||
EnableCode bool
|
||||
EnableWiki bool
|
||||
GloballyWriteableWiki bool
|
||||
EnableExternalWiki bool
|
||||
ExternalWikiURL string
|
||||
EnableIssues bool
|
||||
|
|
|
@ -318,6 +318,16 @@
|
|||
<label>{{ctx.Locale.Tr "repo.settings.use_internal_wiki"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
{{if (not .Repository.IsPrivate)}}
|
||||
<div class="field {{if (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}disabled{{end}}">
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input name="globally_writeable_wiki" type="checkbox" {{if .Permission.IsGloballyWriteable $.UnitTypeWiki}}checked{{end}}>
|
||||
<label>{{ctx.Locale.Tr "repo.settings.wiki_globally_editable"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox{{if $isExternalWikiGlobalDisabled}} disabled{{end}}"{{if $isExternalWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
|
||||
<input class="enable-system-radio" name="enable_external_wiki" type="radio" value="true" data-target="#external_wiki_box" {{if .Repository.UnitEnabled $.Context $.UnitTypeExternalWiki}}checked{{end}}>
|
||||
|
|
|
@ -4,12 +4,16 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
unit_model "code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/tests"
|
||||
|
||||
|
@ -209,6 +213,53 @@ func TestAPIEditWikiPage(t *testing.T) {
|
|||
MakeRequest(t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
func TestAPIEditOtherWikiPage(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
// (drive-by-user) user, session, and token for a drive-by wiki editor
|
||||
username := "drive-by-user"
|
||||
req := NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{
|
||||
"user_name": username,
|
||||
"email": "drive-by@example.com",
|
||||
"password": "examplePassword!1",
|
||||
"retype": "examplePassword!1",
|
||||
})
|
||||
MakeRequest(t, req, http.StatusSeeOther)
|
||||
session := loginUserWithPassword(t, username, "examplePassword!1")
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||
|
||||
// (user2) user for the user whose wiki we're going to edit (as drive-by-user)
|
||||
otherUsername := "user2"
|
||||
|
||||
// Creating a new Wiki page on user2's repo as user1 fails
|
||||
testCreateWiki := func(expectedStatusCode int) {
|
||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/new?token=%s", otherUsername, "repo1", token)
|
||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateWikiPageOptions{
|
||||
Title: "Globally Edited Page",
|
||||
ContentBase64: base64.StdEncoding.EncodeToString([]byte("Wiki page content for API unit tests")),
|
||||
Message: "",
|
||||
})
|
||||
session.MakeRequest(t, req, expectedStatusCode)
|
||||
}
|
||||
testCreateWiki(http.StatusForbidden)
|
||||
|
||||
// Update the repo settings for user2's repo to enable globally writeable wiki
|
||||
ctx := context.Background()
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
var units []repo_model.RepoUnit
|
||||
units = append(units, repo_model.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: unit_model.TypeWiki,
|
||||
Config: new(repo_model.UnitConfig),
|
||||
DefaultPermissions: repo_model.UnitAccessModeWrite,
|
||||
})
|
||||
err := repo_model.UpdateRepositoryUnits(ctx, repo, units, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Creating a new Wiki page on user2's repo works now
|
||||
testCreateWiki(http.StatusCreated)
|
||||
}
|
||||
|
||||
func TestAPIListPageRevisions(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
username := "user2"
|
||||
|
|
Loading…
Add table
Reference in a new issue