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