437 lines · 10.0 KB · Go
RawDownload
// Copyright 2017 The Gitea Authors. All rights reserved.// SPDX-License-Identifier: MIT package unit import (	"errors"	"fmt"	"strings"	"sync/atomic" 	"forgejo.org/models/perm"	"forgejo.org/modules/container"	"forgejo.org/modules/log"	"forgejo.org/modules/setting") // Type is Unit's Typetype Type int // Enumerate all the unit typesconst (	TypeInvalid         Type = iota // 0 invalid	TypeCode                        // 1 code	TypeIssues                      // 2 issues	TypePullRequests                // 3 PRs	TypeReleases                    // 4 Releases	TypeWiki                        // 5 Wiki	TypeExternalWiki                // 6 ExternalWiki	TypeExternalTracker             // 7 ExternalTracker	TypeProjects                    // 8 Projects	TypePackages                    // 9 Packages	TypeActions                     // 10 Actions) // Value returns integer value for unit typefunc (u Type) Value() int {	return int(u)} func (u Type) String() string {	switch u {	case TypeCode:		return "TypeCode"	case TypeIssues:		return "TypeIssues"	case TypePullRequests:		return "TypePullRequests"	case TypeReleases:		return "TypeReleases"	case TypeWiki:		return "TypeWiki"	case TypeExternalWiki:		return "TypeExternalWiki"	case TypeExternalTracker:		return "TypeExternalTracker"	case TypeProjects:		return "TypeProjects"	case TypePackages:		return "TypePackages"	case TypeActions:		return "TypeActions"	}	return fmt.Sprintf("Unknown Type %d", u)} func (u Type) LogString() string {	return fmt.Sprintf("<UnitType:%d:%s>", u, u.String())} var (	// AllRepoUnitTypes contains all the unit types	AllRepoUnitTypes = []Type{		TypeCode,		TypeIssues,		TypePullRequests,		TypeReleases,		TypeWiki,		TypeExternalWiki,		TypeExternalTracker,		TypeProjects,		TypePackages,		TypeActions,	} 	// DefaultRepoUnits contains the default unit types	DefaultRepoUnits = []Type{		TypeCode,		TypeIssues,		TypePullRequests,		TypeReleases,		TypeWiki,		TypeProjects,		TypePackages,		TypeActions,	} 	// ForkRepoUnits contains the default unit types for forks	DefaultForkRepoUnits = []Type{		TypeCode,		TypePullRequests,	} 	// NotAllowedDefaultRepoUnits contains units that can't be default	NotAllowedDefaultRepoUnits = []Type{		TypeExternalWiki,		TypeExternalTracker,	} 	disabledRepoUnitsAtomic atomic.Pointer[[]Type] // the units that have been globally disabled 	// AllowedRepoUnitGroups contains the units that have been globally enabled,	// with mutually exclusive units grouped together.	AllowedRepoUnitGroups = [][]Type{}) // DisabledRepoUnitsGet returns the globally disabled units, it is a quick patch to fix data-race during testing.// Because the queue worker might read when a test is mocking the value. FIXME: refactor to a clear solution later.func DisabledRepoUnitsGet() []Type {	v := disabledRepoUnitsAtomic.Load()	if v == nil {		return nil	}	return *v} func DisabledRepoUnitsSet(v []Type) {	disabledRepoUnitsAtomic.Store(&v)} // Get valid set of default repository units from settingsfunc validateDefaultRepoUnits(defaultUnits, settingDefaultUnits []Type) []Type {	units := defaultUnits 	// Use setting if not empty	if len(settingDefaultUnits) > 0 {		units = make([]Type, 0, len(settingDefaultUnits))		for _, settingUnit := range settingDefaultUnits {			if !settingUnit.CanBeDefault() {				log.Warn("Not allowed as default unit: %s", settingUnit.String())				continue			}			units = append(units, settingUnit)		}	} 	// Remove disabled units	for _, disabledUnit := range DisabledRepoUnitsGet() {		for i, unit := range units {			if unit == disabledUnit {				units = append(units[:i], units[i+1:]...)			}		}	} 	return units} // LoadUnitConfig load units from settingsfunc LoadUnitConfig() error {	disabledRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DisabledRepoUnits...)	if len(invalidKeys) > 0 {		log.Warn("Invalid keys in disabled repo units: %s", strings.Join(invalidKeys, ", "))	}	DisabledRepoUnitsSet(disabledRepoUnits) 	setDefaultRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DefaultRepoUnits...)	if len(invalidKeys) > 0 {		log.Warn("Invalid keys in default repo units: %s", strings.Join(invalidKeys, ", "))	}	DefaultRepoUnits = validateDefaultRepoUnits(DefaultRepoUnits, setDefaultRepoUnits)	if len(DefaultRepoUnits) == 0 {		return errors.New("no default repository units found")	}	setDefaultForkRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DefaultForkRepoUnits...)	if len(invalidKeys) > 0 {		log.Warn("Invalid keys in default fork repo units: %s", strings.Join(invalidKeys, ", "))	}	DefaultForkRepoUnits = validateDefaultRepoUnits(DefaultForkRepoUnits, setDefaultForkRepoUnits)	if len(DefaultForkRepoUnits) == 0 {		return errors.New("no default fork repository units found")	} 	// Collect the allowed repo unit groups. Mutually exclusive units are	// grouped together.	AllowedRepoUnitGroups = [][]Type{}	for _, unit := range []Type{		TypeCode,		TypePullRequests,		TypeProjects,		TypePackages,		TypeActions,	} {		// If unit is globally disabled, ignore it.		if unit.UnitGlobalDisabled() {			continue		} 		// If it is allowed, add it to the group list.		AllowedRepoUnitGroups = append(AllowedRepoUnitGroups, []Type{unit})	} 	addMutuallyExclusiveGroup := func(unit1, unit2 Type) {		var list []Type 		if !unit1.UnitGlobalDisabled() {			list = append(list, unit1)		} 		if !unit2.UnitGlobalDisabled() {			list = append(list, unit2)		} 		if len(list) > 0 {			AllowedRepoUnitGroups = append(AllowedRepoUnitGroups, list)		}	} 	addMutuallyExclusiveGroup(TypeIssues, TypeExternalTracker)	addMutuallyExclusiveGroup(TypeWiki, TypeExternalWiki) 	return nil} // UnitGlobalDisabled checks if unit type is global disabledfunc (u Type) UnitGlobalDisabled() bool {	for _, ud := range DisabledRepoUnitsGet() {		if u == ud {			return true		}	}	return false} // CanBeDefault checks if the unit type can be a default repo unitfunc (u *Type) CanBeDefault() bool {	for _, nadU := range NotAllowedDefaultRepoUnits {		if *u == nadU {			return false		}	}	return true} // Unit is a section of one repositorytype Unit struct {	Type          Type	Name          string	NameKey       string	URI           string	DescKey       string	Idx           int	MaxAccessMode perm.AccessMode // The max access mode of the unit. i.e. Read means this unit can only be read.} // IsLessThan compares order of two unitsfunc (u Unit) IsLessThan(unit Unit) bool {	if (u.Type == TypeExternalTracker || u.Type == TypeExternalWiki) && unit.Type != TypeExternalTracker && unit.Type != TypeExternalWiki {		return false	}	return u.Idx < unit.Idx} // MaxPerm returns the max perms of this unitfunc (u Unit) MaxPerm() perm.AccessMode {	if u.Type == TypeExternalTracker || u.Type == TypeExternalWiki {		return perm.AccessModeRead	}	return perm.AccessModeAdmin} // Enumerate all the unitsvar (	UnitCode = Unit{		TypeCode,		"code",		"repo.code",		"/",		"repo.code.desc",		0,		perm.AccessModeOwner,	} 	UnitIssues = Unit{		TypeIssues,		"issues",		"repo.issues",		"/issues",		"repo.issues.desc",		1,		perm.AccessModeOwner,	} 	UnitExternalTracker = Unit{		TypeExternalTracker,		"ext_issues",		"repo.ext_issues",		"/issues",		"repo.ext_issues.desc",		1,		perm.AccessModeRead,	} 	UnitPullRequests = Unit{		TypePullRequests,		"pulls",		"repo.pulls",		"/pulls",		"repo.pulls.desc",		2,		perm.AccessModeOwner,	} 	UnitReleases = Unit{		TypeReleases,		"releases",		"repo.releases",		"/releases",		"repo.releases.desc",		3,		perm.AccessModeOwner,	} 	UnitWiki = Unit{		TypeWiki,		"wiki",		"repo.wiki",		"/wiki",		"repo.wiki.desc",		4,		perm.AccessModeOwner,	} 	UnitExternalWiki = Unit{		TypeExternalWiki,		"ext_wiki",		"repo.ext_wiki",		"/wiki",		"repo.ext_wiki.desc",		4,		perm.AccessModeRead,	} 	UnitProjects = Unit{		TypeProjects,		"projects",		"repo.projects",		"/projects",		"repo.projects.desc",		5,		perm.AccessModeOwner,	} 	UnitPackages = Unit{		TypePackages,		"packages",		"repo.packages",		"/packages",		"packages.desc",		6,		perm.AccessModeRead,	} 	UnitActions = Unit{		TypeActions,		"actions",		"repo.actions",		"/actions",		"actions.unit.desc",		7,		perm.AccessModeOwner,	} 	// Units contains all the units	Units = map[Type]Unit{		TypeCode:            UnitCode,		TypeIssues:          UnitIssues,		TypeExternalTracker: UnitExternalTracker,		TypePullRequests:    UnitPullRequests,		TypeReleases:        UnitReleases,		TypeWiki:            UnitWiki,		TypeExternalWiki:    UnitExternalWiki,		TypeProjects:        UnitProjects,		TypePackages:        UnitPackages,		TypeActions:         UnitActions,	}) // FindUnitTypes give the unit key names and return valid unique units and invalid keysfunc FindUnitTypes(nameKeys ...string) (res []Type, invalidKeys []string) {	m := make(container.Set[Type])	for _, key := range nameKeys {		t := TypeFromKey(key)		if t == TypeInvalid {			invalidKeys = append(invalidKeys, key)		} else if m.Add(t) {			res = append(res, t)		}	}	return res, invalidKeys} // TypeFromKey give the unit key name and return unitfunc TypeFromKey(nameKey string) Type {	for t, u := range Units {		if strings.EqualFold(nameKey, u.NameKey) {			return t		}	}	return TypeInvalid} // AllUnitKeyNames returns all unit key namesfunc AllUnitKeyNames() []string {	res := make([]string, 0, len(Units))	for _, u := range Units {		res = append(res, u.NameKey)	}	return res} // MinUnitAccessMode returns the minial permission of the permission mapfunc MinUnitAccessMode(unitsMap map[Type]perm.AccessMode) perm.AccessMode {	res := perm.AccessModeNone	for t, mode := range unitsMap {		// Don't allow `TypeExternal{Tracker,Wiki}` to influence this as they can only be set to READ perms.		if t == TypeExternalTracker || t == TypeExternalWiki {			continue		} 		// get the minial permission great than AccessModeNone except all are AccessModeNone		if mode > perm.AccessModeNone && (res == perm.AccessModeNone || mode < res) {			res = mode		}	}	return res}