You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

v259.go 15KB

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