code.gitea.io/gitea@v1.21.7/models/unit/unit.go (about)

     1  // Copyright 2017 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package unit
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"strings"
    10  
    11  	"code.gitea.io/gitea/models/perm"
    12  	"code.gitea.io/gitea/modules/container"
    13  	"code.gitea.io/gitea/modules/log"
    14  	"code.gitea.io/gitea/modules/setting"
    15  )
    16  
    17  // Type is Unit's Type
    18  type Type int
    19  
    20  // Enumerate all the unit types
    21  const (
    22  	TypeInvalid         Type = iota // 0 invalid
    23  	TypeCode                        // 1 code
    24  	TypeIssues                      // 2 issues
    25  	TypePullRequests                // 3 PRs
    26  	TypeReleases                    // 4 Releases
    27  	TypeWiki                        // 5 Wiki
    28  	TypeExternalWiki                // 6 ExternalWiki
    29  	TypeExternalTracker             // 7 ExternalTracker
    30  	TypeProjects                    // 8 Kanban board
    31  	TypePackages                    // 9 Packages
    32  	TypeActions                     // 10 Actions
    33  )
    34  
    35  // Value returns integer value for unit type
    36  func (u Type) Value() int {
    37  	return int(u)
    38  }
    39  
    40  func (u Type) String() string {
    41  	switch u {
    42  	case TypeCode:
    43  		return "TypeCode"
    44  	case TypeIssues:
    45  		return "TypeIssues"
    46  	case TypePullRequests:
    47  		return "TypePullRequests"
    48  	case TypeReleases:
    49  		return "TypeReleases"
    50  	case TypeWiki:
    51  		return "TypeWiki"
    52  	case TypeExternalWiki:
    53  		return "TypeExternalWiki"
    54  	case TypeExternalTracker:
    55  		return "TypeExternalTracker"
    56  	case TypeProjects:
    57  		return "TypeProjects"
    58  	case TypePackages:
    59  		return "TypePackages"
    60  	case TypeActions:
    61  		return "TypeActions"
    62  	}
    63  	return fmt.Sprintf("Unknown Type %d", u)
    64  }
    65  
    66  func (u Type) LogString() string {
    67  	return fmt.Sprintf("<UnitType:%d:%s>", u, u.String())
    68  }
    69  
    70  var (
    71  	// AllRepoUnitTypes contains all the unit types
    72  	AllRepoUnitTypes = []Type{
    73  		TypeCode,
    74  		TypeIssues,
    75  		TypePullRequests,
    76  		TypeReleases,
    77  		TypeWiki,
    78  		TypeExternalWiki,
    79  		TypeExternalTracker,
    80  		TypeProjects,
    81  		TypePackages,
    82  		TypeActions,
    83  	}
    84  
    85  	// DefaultRepoUnits contains the default unit types
    86  	DefaultRepoUnits = []Type{
    87  		TypeCode,
    88  		TypeIssues,
    89  		TypePullRequests,
    90  		TypeReleases,
    91  		TypeWiki,
    92  		TypeProjects,
    93  		TypePackages,
    94  	}
    95  
    96  	// ForkRepoUnits contains the default unit types for forks
    97  	DefaultForkRepoUnits = []Type{
    98  		TypeCode,
    99  		TypePullRequests,
   100  	}
   101  
   102  	// NotAllowedDefaultRepoUnits contains units that can't be default
   103  	NotAllowedDefaultRepoUnits = []Type{
   104  		TypeExternalWiki,
   105  		TypeExternalTracker,
   106  	}
   107  
   108  	// DisabledRepoUnits contains the units that have been globally disabled
   109  	DisabledRepoUnits = []Type{}
   110  )
   111  
   112  // Get valid set of default repository units from settings
   113  func validateDefaultRepoUnits(defaultUnits, settingDefaultUnits []Type) []Type {
   114  	units := defaultUnits
   115  
   116  	// Use setting if not empty
   117  	if len(settingDefaultUnits) > 0 {
   118  		units = make([]Type, 0, len(settingDefaultUnits))
   119  		for _, settingUnit := range settingDefaultUnits {
   120  			if !settingUnit.CanBeDefault() {
   121  				log.Warn("Not allowed as default unit: %s", settingUnit.String())
   122  				continue
   123  			}
   124  			units = append(units, settingUnit)
   125  		}
   126  	}
   127  
   128  	// Remove disabled units
   129  	for _, disabledUnit := range DisabledRepoUnits {
   130  		for i, unit := range units {
   131  			if unit == disabledUnit {
   132  				units = append(units[:i], units[i+1:]...)
   133  			}
   134  		}
   135  	}
   136  
   137  	return units
   138  }
   139  
   140  // LoadUnitConfig load units from settings
   141  func LoadUnitConfig() error {
   142  	var invalidKeys []string
   143  	DisabledRepoUnits, invalidKeys = FindUnitTypes(setting.Repository.DisabledRepoUnits...)
   144  	if len(invalidKeys) > 0 {
   145  		log.Warn("Invalid keys in disabled repo units: %s", strings.Join(invalidKeys, ", "))
   146  	}
   147  
   148  	setDefaultRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DefaultRepoUnits...)
   149  	if len(invalidKeys) > 0 {
   150  		log.Warn("Invalid keys in default repo units: %s", strings.Join(invalidKeys, ", "))
   151  	}
   152  	DefaultRepoUnits = validateDefaultRepoUnits(DefaultRepoUnits, setDefaultRepoUnits)
   153  	if len(DefaultRepoUnits) == 0 {
   154  		return errors.New("no default repository units found")
   155  	}
   156  	setDefaultForkRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DefaultForkRepoUnits...)
   157  	if len(invalidKeys) > 0 {
   158  		log.Warn("Invalid keys in default fork repo units: %s", strings.Join(invalidKeys, ", "))
   159  	}
   160  	DefaultForkRepoUnits = validateDefaultRepoUnits(DefaultForkRepoUnits, setDefaultForkRepoUnits)
   161  	if len(DefaultForkRepoUnits) == 0 {
   162  		return errors.New("no default fork repository units found")
   163  	}
   164  	return nil
   165  }
   166  
   167  // UnitGlobalDisabled checks if unit type is global disabled
   168  func (u Type) UnitGlobalDisabled() bool {
   169  	for _, ud := range DisabledRepoUnits {
   170  		if u == ud {
   171  			return true
   172  		}
   173  	}
   174  	return false
   175  }
   176  
   177  // CanBeDefault checks if the unit type can be a default repo unit
   178  func (u *Type) CanBeDefault() bool {
   179  	for _, nadU := range NotAllowedDefaultRepoUnits {
   180  		if *u == nadU {
   181  			return false
   182  		}
   183  	}
   184  	return true
   185  }
   186  
   187  // Unit is a section of one repository
   188  type Unit struct {
   189  	Type          Type
   190  	NameKey       string
   191  	URI           string
   192  	DescKey       string
   193  	Idx           int
   194  	MaxAccessMode perm.AccessMode // The max access mode of the unit. i.e. Read means this unit can only be read.
   195  }
   196  
   197  // IsLessThan compares order of two units
   198  func (u Unit) IsLessThan(unit Unit) bool {
   199  	if (u.Type == TypeExternalTracker || u.Type == TypeExternalWiki) && unit.Type != TypeExternalTracker && unit.Type != TypeExternalWiki {
   200  		return false
   201  	}
   202  	return u.Idx < unit.Idx
   203  }
   204  
   205  // MaxPerm returns the max perms of this unit
   206  func (u Unit) MaxPerm() perm.AccessMode {
   207  	if u.Type == TypeExternalTracker || u.Type == TypeExternalWiki {
   208  		return perm.AccessModeRead
   209  	}
   210  	return perm.AccessModeAdmin
   211  }
   212  
   213  // Enumerate all the units
   214  var (
   215  	UnitCode = Unit{
   216  		TypeCode,
   217  		"repo.code",
   218  		"/",
   219  		"repo.code.desc",
   220  		0,
   221  		perm.AccessModeOwner,
   222  	}
   223  
   224  	UnitIssues = Unit{
   225  		TypeIssues,
   226  		"repo.issues",
   227  		"/issues",
   228  		"repo.issues.desc",
   229  		1,
   230  		perm.AccessModeOwner,
   231  	}
   232  
   233  	UnitExternalTracker = Unit{
   234  		TypeExternalTracker,
   235  		"repo.ext_issues",
   236  		"/issues",
   237  		"repo.ext_issues.desc",
   238  		1,
   239  		perm.AccessModeRead,
   240  	}
   241  
   242  	UnitPullRequests = Unit{
   243  		TypePullRequests,
   244  		"repo.pulls",
   245  		"/pulls",
   246  		"repo.pulls.desc",
   247  		2,
   248  		perm.AccessModeOwner,
   249  	}
   250  
   251  	UnitReleases = Unit{
   252  		TypeReleases,
   253  		"repo.releases",
   254  		"/releases",
   255  		"repo.releases.desc",
   256  		3,
   257  		perm.AccessModeOwner,
   258  	}
   259  
   260  	UnitWiki = Unit{
   261  		TypeWiki,
   262  		"repo.wiki",
   263  		"/wiki",
   264  		"repo.wiki.desc",
   265  		4,
   266  		perm.AccessModeOwner,
   267  	}
   268  
   269  	UnitExternalWiki = Unit{
   270  		TypeExternalWiki,
   271  		"repo.ext_wiki",
   272  		"/wiki",
   273  		"repo.ext_wiki.desc",
   274  		4,
   275  		perm.AccessModeRead,
   276  	}
   277  
   278  	UnitProjects = Unit{
   279  		TypeProjects,
   280  		"repo.projects",
   281  		"/projects",
   282  		"repo.projects.desc",
   283  		5,
   284  		perm.AccessModeOwner,
   285  	}
   286  
   287  	UnitPackages = Unit{
   288  		TypePackages,
   289  		"repo.packages",
   290  		"/packages",
   291  		"packages.desc",
   292  		6,
   293  		perm.AccessModeRead,
   294  	}
   295  
   296  	UnitActions = Unit{
   297  		TypeActions,
   298  		"repo.actions",
   299  		"/actions",
   300  		"actions.unit.desc",
   301  		7,
   302  		perm.AccessModeOwner,
   303  	}
   304  
   305  	// Units contains all the units
   306  	Units = map[Type]Unit{
   307  		TypeCode:            UnitCode,
   308  		TypeIssues:          UnitIssues,
   309  		TypeExternalTracker: UnitExternalTracker,
   310  		TypePullRequests:    UnitPullRequests,
   311  		TypeReleases:        UnitReleases,
   312  		TypeWiki:            UnitWiki,
   313  		TypeExternalWiki:    UnitExternalWiki,
   314  		TypeProjects:        UnitProjects,
   315  		TypePackages:        UnitPackages,
   316  		TypeActions:         UnitActions,
   317  	}
   318  )
   319  
   320  // FindUnitTypes give the unit key names and return valid unique units and invalid keys
   321  func FindUnitTypes(nameKeys ...string) (res []Type, invalidKeys []string) {
   322  	m := make(container.Set[Type])
   323  	for _, key := range nameKeys {
   324  		t := TypeFromKey(key)
   325  		if t == TypeInvalid {
   326  			invalidKeys = append(invalidKeys, key)
   327  		} else if m.Add(t) {
   328  			res = append(res, t)
   329  		}
   330  	}
   331  	return res, invalidKeys
   332  }
   333  
   334  // TypeFromKey give the unit key name and return unit
   335  func TypeFromKey(nameKey string) Type {
   336  	for t, u := range Units {
   337  		if strings.EqualFold(nameKey, u.NameKey) {
   338  			return t
   339  		}
   340  	}
   341  	return TypeInvalid
   342  }
   343  
   344  // AllUnitKeyNames returns all unit key names
   345  func AllUnitKeyNames() []string {
   346  	res := make([]string, 0, len(Units))
   347  	for _, u := range Units {
   348  		res = append(res, u.NameKey)
   349  	}
   350  	return res
   351  }
   352  
   353  // MinUnitAccessMode returns the minial permission of the permission map
   354  func MinUnitAccessMode(unitsMap map[Type]perm.AccessMode) perm.AccessMode {
   355  	res := perm.AccessModeNone
   356  	for t, mode := range unitsMap {
   357  		// Don't allow `TypeExternal{Tracker,Wiki}` to influence this as they can only be set to READ perms.
   358  		if t == TypeExternalTracker || t == TypeExternalWiki {
   359  			continue
   360  		}
   361  
   362  		// get the minial permission great than AccessModeNone except all are AccessModeNone
   363  		if mode > perm.AccessModeNone && (res == perm.AccessModeNone || mode < res) {
   364  			res = mode
   365  		}
   366  	}
   367  	return res
   368  }