code.gitea.io/gitea@v1.21.7/models/migrations/v1_20/v259.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package v1_20 //nolint 5 6 import ( 7 "fmt" 8 "strings" 9 10 "code.gitea.io/gitea/modules/log" 11 12 "xorm.io/xorm" 13 ) 14 15 // unknownAccessTokenScope represents the scope for an access token that isn't 16 // known be an old token or a new token. 17 type unknownAccessTokenScope string 18 19 // AccessTokenScope represents the scope for an access token. 20 type AccessTokenScope string 21 22 // for all categories, write implies read 23 const ( 24 AccessTokenScopeAll AccessTokenScope = "all" 25 AccessTokenScopePublicOnly AccessTokenScope = "public-only" // limited to public orgs/repos 26 27 AccessTokenScopeReadActivityPub AccessTokenScope = "read:activitypub" 28 AccessTokenScopeWriteActivityPub AccessTokenScope = "write:activitypub" 29 30 AccessTokenScopeReadAdmin AccessTokenScope = "read:admin" 31 AccessTokenScopeWriteAdmin AccessTokenScope = "write:admin" 32 33 AccessTokenScopeReadMisc AccessTokenScope = "read:misc" 34 AccessTokenScopeWriteMisc AccessTokenScope = "write:misc" 35 36 AccessTokenScopeReadNotification AccessTokenScope = "read:notification" 37 AccessTokenScopeWriteNotification AccessTokenScope = "write:notification" 38 39 AccessTokenScopeReadOrganization AccessTokenScope = "read:organization" 40 AccessTokenScopeWriteOrganization AccessTokenScope = "write:organization" 41 42 AccessTokenScopeReadPackage AccessTokenScope = "read:package" 43 AccessTokenScopeWritePackage AccessTokenScope = "write:package" 44 45 AccessTokenScopeReadIssue AccessTokenScope = "read:issue" 46 AccessTokenScopeWriteIssue AccessTokenScope = "write:issue" 47 48 AccessTokenScopeReadRepository AccessTokenScope = "read:repository" 49 AccessTokenScopeWriteRepository AccessTokenScope = "write:repository" 50 51 AccessTokenScopeReadUser AccessTokenScope = "read:user" 52 AccessTokenScopeWriteUser AccessTokenScope = "write:user" 53 ) 54 55 // accessTokenScopeBitmap represents a bitmap of access token scopes. 56 type accessTokenScopeBitmap uint64 57 58 // Bitmap of each scope, including the child scopes. 59 const ( 60 // AccessTokenScopeAllBits is the bitmap of all access token scopes 61 accessTokenScopeAllBits accessTokenScopeBitmap = accessTokenScopeWriteActivityPubBits | 62 accessTokenScopeWriteAdminBits | accessTokenScopeWriteMiscBits | accessTokenScopeWriteNotificationBits | 63 accessTokenScopeWriteOrganizationBits | accessTokenScopeWritePackageBits | accessTokenScopeWriteIssueBits | 64 accessTokenScopeWriteRepositoryBits | accessTokenScopeWriteUserBits 65 66 accessTokenScopePublicOnlyBits accessTokenScopeBitmap = 1 << iota 67 68 accessTokenScopeReadActivityPubBits accessTokenScopeBitmap = 1 << iota 69 accessTokenScopeWriteActivityPubBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadActivityPubBits 70 71 accessTokenScopeReadAdminBits accessTokenScopeBitmap = 1 << iota 72 accessTokenScopeWriteAdminBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadAdminBits 73 74 accessTokenScopeReadMiscBits accessTokenScopeBitmap = 1 << iota 75 accessTokenScopeWriteMiscBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadMiscBits 76 77 accessTokenScopeReadNotificationBits accessTokenScopeBitmap = 1 << iota 78 accessTokenScopeWriteNotificationBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadNotificationBits 79 80 accessTokenScopeReadOrganizationBits accessTokenScopeBitmap = 1 << iota 81 accessTokenScopeWriteOrganizationBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadOrganizationBits 82 83 accessTokenScopeReadPackageBits accessTokenScopeBitmap = 1 << iota 84 accessTokenScopeWritePackageBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadPackageBits 85 86 accessTokenScopeReadIssueBits accessTokenScopeBitmap = 1 << iota 87 accessTokenScopeWriteIssueBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadIssueBits 88 89 accessTokenScopeReadRepositoryBits accessTokenScopeBitmap = 1 << iota 90 accessTokenScopeWriteRepositoryBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadRepositoryBits 91 92 accessTokenScopeReadUserBits accessTokenScopeBitmap = 1 << iota 93 accessTokenScopeWriteUserBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadUserBits 94 95 // The current implementation only supports up to 64 token scopes. 96 // If we need to support > 64 scopes, 97 // refactoring the whole implementation in this file (and only this file) is needed. 98 ) 99 100 // allAccessTokenScopes contains all access token scopes. 101 // The order is important: parent scope must precede child scopes. 102 var allAccessTokenScopes = []AccessTokenScope{ 103 AccessTokenScopePublicOnly, 104 AccessTokenScopeWriteActivityPub, AccessTokenScopeReadActivityPub, 105 AccessTokenScopeWriteAdmin, AccessTokenScopeReadAdmin, 106 AccessTokenScopeWriteMisc, AccessTokenScopeReadMisc, 107 AccessTokenScopeWriteNotification, AccessTokenScopeReadNotification, 108 AccessTokenScopeWriteOrganization, AccessTokenScopeReadOrganization, 109 AccessTokenScopeWritePackage, AccessTokenScopeReadPackage, 110 AccessTokenScopeWriteIssue, AccessTokenScopeReadIssue, 111 AccessTokenScopeWriteRepository, AccessTokenScopeReadRepository, 112 AccessTokenScopeWriteUser, AccessTokenScopeReadUser, 113 } 114 115 // allAccessTokenScopeBits contains all access token scopes. 116 var allAccessTokenScopeBits = map[AccessTokenScope]accessTokenScopeBitmap{ 117 AccessTokenScopeAll: accessTokenScopeAllBits, 118 AccessTokenScopePublicOnly: accessTokenScopePublicOnlyBits, 119 AccessTokenScopeReadActivityPub: accessTokenScopeReadActivityPubBits, 120 AccessTokenScopeWriteActivityPub: accessTokenScopeWriteActivityPubBits, 121 AccessTokenScopeReadAdmin: accessTokenScopeReadAdminBits, 122 AccessTokenScopeWriteAdmin: accessTokenScopeWriteAdminBits, 123 AccessTokenScopeReadMisc: accessTokenScopeReadMiscBits, 124 AccessTokenScopeWriteMisc: accessTokenScopeWriteMiscBits, 125 AccessTokenScopeReadNotification: accessTokenScopeReadNotificationBits, 126 AccessTokenScopeWriteNotification: accessTokenScopeWriteNotificationBits, 127 AccessTokenScopeReadOrganization: accessTokenScopeReadOrganizationBits, 128 AccessTokenScopeWriteOrganization: accessTokenScopeWriteOrganizationBits, 129 AccessTokenScopeReadPackage: accessTokenScopeReadPackageBits, 130 AccessTokenScopeWritePackage: accessTokenScopeWritePackageBits, 131 AccessTokenScopeReadIssue: accessTokenScopeReadIssueBits, 132 AccessTokenScopeWriteIssue: accessTokenScopeWriteIssueBits, 133 AccessTokenScopeReadRepository: accessTokenScopeReadRepositoryBits, 134 AccessTokenScopeWriteRepository: accessTokenScopeWriteRepositoryBits, 135 AccessTokenScopeReadUser: accessTokenScopeReadUserBits, 136 AccessTokenScopeWriteUser: accessTokenScopeWriteUserBits, 137 } 138 139 // hasScope returns true if the string has the given scope 140 func (bitmap accessTokenScopeBitmap) hasScope(scope AccessTokenScope) (bool, error) { 141 expectedBits, ok := allAccessTokenScopeBits[scope] 142 if !ok { 143 return false, fmt.Errorf("invalid access token scope: %s", scope) 144 } 145 146 return bitmap&expectedBits == expectedBits, nil 147 } 148 149 // toScope returns a normalized scope string without any duplicates. 150 func (bitmap accessTokenScopeBitmap) toScope(unknownScopes *[]unknownAccessTokenScope) AccessTokenScope { 151 var scopes []string 152 153 // Preserve unknown scopes, and put them at the beginning so that it's clear 154 // when debugging. 155 if unknownScopes != nil { 156 for _, unknownScope := range *unknownScopes { 157 scopes = append(scopes, string(unknownScope)) 158 } 159 } 160 161 // iterate over all scopes, and reconstruct the bitmap 162 // if the reconstructed bitmap doesn't change, then the scope is already included 163 var reconstruct accessTokenScopeBitmap 164 165 for _, singleScope := range allAccessTokenScopes { 166 // no need for error checking here, since we know the scope is valid 167 if ok, _ := bitmap.hasScope(singleScope); ok { 168 current := reconstruct | allAccessTokenScopeBits[singleScope] 169 if current == reconstruct { 170 continue 171 } 172 173 reconstruct = current 174 scopes = append(scopes, string(singleScope)) 175 } 176 } 177 178 scope := AccessTokenScope(strings.Join(scopes, ",")) 179 scope = AccessTokenScope(strings.ReplaceAll( 180 string(scope), 181 "write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user", 182 "all", 183 )) 184 return scope 185 } 186 187 // parse the scope string into a bitmap, thus removing possible duplicates. 188 func (s AccessTokenScope) parse() (accessTokenScopeBitmap, *[]unknownAccessTokenScope) { 189 var bitmap accessTokenScopeBitmap 190 var unknownScopes []unknownAccessTokenScope 191 192 // The following is the more performant equivalent of 'for _, v := range strings.Split(remainingScope, ",")' as this is hot code 193 remainingScopes := string(s) 194 for len(remainingScopes) > 0 { 195 i := strings.IndexByte(remainingScopes, ',') 196 var v string 197 if i < 0 { 198 v = remainingScopes 199 remainingScopes = "" 200 } else if i+1 >= len(remainingScopes) { 201 v = remainingScopes[:i] 202 remainingScopes = "" 203 } else { 204 v = remainingScopes[:i] 205 remainingScopes = remainingScopes[i+1:] 206 } 207 singleScope := AccessTokenScope(v) 208 if singleScope == "" { 209 continue 210 } 211 if singleScope == AccessTokenScopeAll { 212 bitmap |= accessTokenScopeAllBits 213 continue 214 } 215 216 bits, ok := allAccessTokenScopeBits[singleScope] 217 if !ok { 218 unknownScopes = append(unknownScopes, unknownAccessTokenScope(string(singleScope))) 219 } 220 bitmap |= bits 221 } 222 223 return bitmap, &unknownScopes 224 } 225 226 // NormalizePreservingUnknown returns a normalized scope string without any 227 // duplicates. Unknown scopes are included. 228 func (s AccessTokenScope) NormalizePreservingUnknown() AccessTokenScope { 229 bitmap, unknownScopes := s.parse() 230 231 return bitmap.toScope(unknownScopes) 232 } 233 234 // OldAccessTokenScope represents the scope for an access token. 235 type OldAccessTokenScope string 236 237 const ( 238 OldAccessTokenScopeAll OldAccessTokenScope = "all" 239 240 OldAccessTokenScopeRepo OldAccessTokenScope = "repo" 241 OldAccessTokenScopeRepoStatus OldAccessTokenScope = "repo:status" 242 OldAccessTokenScopePublicRepo OldAccessTokenScope = "public_repo" 243 244 OldAccessTokenScopeAdminOrg OldAccessTokenScope = "admin:org" 245 OldAccessTokenScopeWriteOrg OldAccessTokenScope = "write:org" 246 OldAccessTokenScopeReadOrg OldAccessTokenScope = "read:org" 247 248 OldAccessTokenScopeAdminPublicKey OldAccessTokenScope = "admin:public_key" 249 OldAccessTokenScopeWritePublicKey OldAccessTokenScope = "write:public_key" 250 OldAccessTokenScopeReadPublicKey OldAccessTokenScope = "read:public_key" 251 252 OldAccessTokenScopeAdminRepoHook OldAccessTokenScope = "admin:repo_hook" 253 OldAccessTokenScopeWriteRepoHook OldAccessTokenScope = "write:repo_hook" 254 OldAccessTokenScopeReadRepoHook OldAccessTokenScope = "read:repo_hook" 255 256 OldAccessTokenScopeAdminOrgHook OldAccessTokenScope = "admin:org_hook" 257 258 OldAccessTokenScopeNotification OldAccessTokenScope = "notification" 259 260 OldAccessTokenScopeUser OldAccessTokenScope = "user" 261 OldAccessTokenScopeReadUser OldAccessTokenScope = "read:user" 262 OldAccessTokenScopeUserEmail OldAccessTokenScope = "user:email" 263 OldAccessTokenScopeUserFollow OldAccessTokenScope = "user:follow" 264 265 OldAccessTokenScopeDeleteRepo OldAccessTokenScope = "delete_repo" 266 267 OldAccessTokenScopePackage OldAccessTokenScope = "package" 268 OldAccessTokenScopeWritePackage OldAccessTokenScope = "write:package" 269 OldAccessTokenScopeReadPackage OldAccessTokenScope = "read:package" 270 OldAccessTokenScopeDeletePackage OldAccessTokenScope = "delete:package" 271 272 OldAccessTokenScopeAdminGPGKey OldAccessTokenScope = "admin:gpg_key" 273 OldAccessTokenScopeWriteGPGKey OldAccessTokenScope = "write:gpg_key" 274 OldAccessTokenScopeReadGPGKey OldAccessTokenScope = "read:gpg_key" 275 276 OldAccessTokenScopeAdminApplication OldAccessTokenScope = "admin:application" 277 OldAccessTokenScopeWriteApplication OldAccessTokenScope = "write:application" 278 OldAccessTokenScopeReadApplication OldAccessTokenScope = "read:application" 279 280 OldAccessTokenScopeSudo OldAccessTokenScope = "sudo" 281 ) 282 283 var accessTokenScopeMap = map[OldAccessTokenScope][]AccessTokenScope{ 284 OldAccessTokenScopeAll: {AccessTokenScopeAll}, 285 OldAccessTokenScopeRepo: {AccessTokenScopeWriteRepository}, 286 OldAccessTokenScopeRepoStatus: {AccessTokenScopeWriteRepository}, 287 OldAccessTokenScopePublicRepo: {AccessTokenScopePublicOnly, AccessTokenScopeWriteRepository}, 288 OldAccessTokenScopeAdminOrg: {AccessTokenScopeWriteOrganization}, 289 OldAccessTokenScopeWriteOrg: {AccessTokenScopeWriteOrganization}, 290 OldAccessTokenScopeReadOrg: {AccessTokenScopeReadOrganization}, 291 OldAccessTokenScopeAdminPublicKey: {AccessTokenScopeWriteUser}, 292 OldAccessTokenScopeWritePublicKey: {AccessTokenScopeWriteUser}, 293 OldAccessTokenScopeReadPublicKey: {AccessTokenScopeReadUser}, 294 OldAccessTokenScopeAdminRepoHook: {AccessTokenScopeWriteRepository}, 295 OldAccessTokenScopeWriteRepoHook: {AccessTokenScopeWriteRepository}, 296 OldAccessTokenScopeReadRepoHook: {AccessTokenScopeReadRepository}, 297 OldAccessTokenScopeAdminOrgHook: {AccessTokenScopeWriteOrganization}, 298 OldAccessTokenScopeNotification: {AccessTokenScopeWriteNotification}, 299 OldAccessTokenScopeUser: {AccessTokenScopeWriteUser}, 300 OldAccessTokenScopeReadUser: {AccessTokenScopeReadUser}, 301 OldAccessTokenScopeUserEmail: {AccessTokenScopeWriteUser}, 302 OldAccessTokenScopeUserFollow: {AccessTokenScopeWriteUser}, 303 OldAccessTokenScopeDeleteRepo: {AccessTokenScopeWriteRepository}, 304 OldAccessTokenScopePackage: {AccessTokenScopeWritePackage}, 305 OldAccessTokenScopeWritePackage: {AccessTokenScopeWritePackage}, 306 OldAccessTokenScopeReadPackage: {AccessTokenScopeReadPackage}, 307 OldAccessTokenScopeDeletePackage: {AccessTokenScopeWritePackage}, 308 OldAccessTokenScopeAdminGPGKey: {AccessTokenScopeWriteUser}, 309 OldAccessTokenScopeWriteGPGKey: {AccessTokenScopeWriteUser}, 310 OldAccessTokenScopeReadGPGKey: {AccessTokenScopeReadUser}, 311 OldAccessTokenScopeAdminApplication: {AccessTokenScopeWriteUser}, 312 OldAccessTokenScopeWriteApplication: {AccessTokenScopeWriteUser}, 313 OldAccessTokenScopeReadApplication: {AccessTokenScopeReadUser}, 314 OldAccessTokenScopeSudo: {AccessTokenScopeWriteAdmin}, 315 } 316 317 type AccessToken struct { 318 ID int64 `xorm:"pk autoincr"` 319 Scope string 320 } 321 322 func ConvertScopedAccessTokens(x *xorm.Engine) error { 323 var tokens []*AccessToken 324 325 if err := x.Find(&tokens); err != nil { 326 return err 327 } 328 329 for _, token := range tokens { 330 var scopes []string 331 allNewScopesMap := make(map[AccessTokenScope]bool) 332 for _, oldScope := range strings.Split(token.Scope, ",") { 333 if newScopes, exists := accessTokenScopeMap[OldAccessTokenScope(oldScope)]; exists { 334 for _, newScope := range newScopes { 335 allNewScopesMap[newScope] = true 336 } 337 } else { 338 log.Debug("access token scope not recognized as old token scope %s; preserving it", oldScope) 339 scopes = append(scopes, oldScope) 340 } 341 } 342 343 for s := range allNewScopesMap { 344 scopes = append(scopes, string(s)) 345 } 346 scope := AccessTokenScope(strings.Join(scopes, ",")) 347 348 // normalize the scope 349 normScope := scope.NormalizePreservingUnknown() 350 351 token.Scope = string(normScope) 352 353 // update the db entry with the new scope 354 if _, err := x.Cols("scope").ID(token.ID).Update(token); err != nil { 355 return err 356 } 357 } 358 359 return nil 360 }