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.

token_scope.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package auth
  4. import (
  5. "fmt"
  6. "strings"
  7. )
  8. // AccessTokenScope represents the scope for an access token.
  9. type AccessTokenScope string
  10. const (
  11. AccessTokenScopeAll AccessTokenScope = "all"
  12. AccessTokenScopeRepo AccessTokenScope = "repo"
  13. AccessTokenScopeRepoStatus AccessTokenScope = "repo:status"
  14. AccessTokenScopePublicRepo AccessTokenScope = "public_repo"
  15. AccessTokenScopeAdminOrg AccessTokenScope = "admin:org"
  16. AccessTokenScopeWriteOrg AccessTokenScope = "write:org"
  17. AccessTokenScopeReadOrg AccessTokenScope = "read:org"
  18. AccessTokenScopeAdminPublicKey AccessTokenScope = "admin:public_key"
  19. AccessTokenScopeWritePublicKey AccessTokenScope = "write:public_key"
  20. AccessTokenScopeReadPublicKey AccessTokenScope = "read:public_key"
  21. AccessTokenScopeAdminRepoHook AccessTokenScope = "admin:repo_hook"
  22. AccessTokenScopeWriteRepoHook AccessTokenScope = "write:repo_hook"
  23. AccessTokenScopeReadRepoHook AccessTokenScope = "read:repo_hook"
  24. AccessTokenScopeAdminOrgHook AccessTokenScope = "admin:org_hook"
  25. AccessTokenScopeNotification AccessTokenScope = "notification"
  26. AccessTokenScopeUser AccessTokenScope = "user"
  27. AccessTokenScopeReadUser AccessTokenScope = "read:user"
  28. AccessTokenScopeUserEmail AccessTokenScope = "user:email"
  29. AccessTokenScopeUserFollow AccessTokenScope = "user:follow"
  30. AccessTokenScopeDeleteRepo AccessTokenScope = "delete_repo"
  31. AccessTokenScopePackage AccessTokenScope = "package"
  32. AccessTokenScopeWritePackage AccessTokenScope = "write:package"
  33. AccessTokenScopeReadPackage AccessTokenScope = "read:package"
  34. AccessTokenScopeDeletePackage AccessTokenScope = "delete:package"
  35. AccessTokenScopeAdminGPGKey AccessTokenScope = "admin:gpg_key"
  36. AccessTokenScopeWriteGPGKey AccessTokenScope = "write:gpg_key"
  37. AccessTokenScopeReadGPGKey AccessTokenScope = "read:gpg_key"
  38. AccessTokenScopeAdminApplication AccessTokenScope = "admin:application"
  39. AccessTokenScopeWriteApplication AccessTokenScope = "write:application"
  40. AccessTokenScopeReadApplication AccessTokenScope = "read:application"
  41. AccessTokenScopeSudo AccessTokenScope = "sudo"
  42. )
  43. // AccessTokenScopeBitmap represents a bitmap of access token scopes.
  44. type AccessTokenScopeBitmap uint64
  45. // Bitmap of each scope, including the child scopes.
  46. const (
  47. // AccessTokenScopeAllBits is the bitmap of all access token scopes, except `sudo`.
  48. AccessTokenScopeAllBits AccessTokenScopeBitmap = AccessTokenScopeRepoBits |
  49. AccessTokenScopeAdminOrgBits | AccessTokenScopeAdminPublicKeyBits | AccessTokenScopeAdminOrgHookBits |
  50. AccessTokenScopeNotificationBits | AccessTokenScopeUserBits | AccessTokenScopeDeleteRepoBits |
  51. AccessTokenScopePackageBits | AccessTokenScopeAdminGPGKeyBits | AccessTokenScopeAdminApplicationBits
  52. AccessTokenScopeRepoBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeRepoStatusBits | AccessTokenScopePublicRepoBits | AccessTokenScopeAdminRepoHookBits
  53. AccessTokenScopeRepoStatusBits AccessTokenScopeBitmap = 1 << iota
  54. AccessTokenScopePublicRepoBits AccessTokenScopeBitmap = 1 << iota
  55. AccessTokenScopeAdminOrgBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteOrgBits
  56. AccessTokenScopeWriteOrgBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadOrgBits
  57. AccessTokenScopeReadOrgBits AccessTokenScopeBitmap = 1 << iota
  58. AccessTokenScopeAdminPublicKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWritePublicKeyBits
  59. AccessTokenScopeWritePublicKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadPublicKeyBits
  60. AccessTokenScopeReadPublicKeyBits AccessTokenScopeBitmap = 1 << iota
  61. AccessTokenScopeAdminRepoHookBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteRepoHookBits
  62. AccessTokenScopeWriteRepoHookBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadRepoHookBits
  63. AccessTokenScopeReadRepoHookBits AccessTokenScopeBitmap = 1 << iota
  64. AccessTokenScopeAdminOrgHookBits AccessTokenScopeBitmap = 1 << iota
  65. AccessTokenScopeNotificationBits AccessTokenScopeBitmap = 1 << iota
  66. AccessTokenScopeUserBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadUserBits | AccessTokenScopeUserEmailBits | AccessTokenScopeUserFollowBits
  67. AccessTokenScopeReadUserBits AccessTokenScopeBitmap = 1 << iota
  68. AccessTokenScopeUserEmailBits AccessTokenScopeBitmap = 1 << iota
  69. AccessTokenScopeUserFollowBits AccessTokenScopeBitmap = 1 << iota
  70. AccessTokenScopeDeleteRepoBits AccessTokenScopeBitmap = 1 << iota
  71. AccessTokenScopePackageBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWritePackageBits | AccessTokenScopeDeletePackageBits
  72. AccessTokenScopeWritePackageBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadPackageBits
  73. AccessTokenScopeReadPackageBits AccessTokenScopeBitmap = 1 << iota
  74. AccessTokenScopeDeletePackageBits AccessTokenScopeBitmap = 1 << iota
  75. AccessTokenScopeAdminGPGKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteGPGKeyBits
  76. AccessTokenScopeWriteGPGKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadGPGKeyBits
  77. AccessTokenScopeReadGPGKeyBits AccessTokenScopeBitmap = 1 << iota
  78. AccessTokenScopeAdminApplicationBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteApplicationBits
  79. AccessTokenScopeWriteApplicationBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadApplicationBits
  80. AccessTokenScopeReadApplicationBits AccessTokenScopeBitmap = 1 << iota
  81. AccessTokenScopeSudoBits AccessTokenScopeBitmap = 1 << iota
  82. // The current implementation only supports up to 64 token scopes.
  83. // If we need to support > 64 scopes,
  84. // refactoring the whole implementation in this file (and only this file) is needed.
  85. )
  86. // allAccessTokenScopes contains all access token scopes.
  87. // The order is important: parent scope must precedes child scopes.
  88. var allAccessTokenScopes = []AccessTokenScope{
  89. AccessTokenScopeRepo, AccessTokenScopeRepoStatus, AccessTokenScopePublicRepo,
  90. AccessTokenScopeAdminOrg, AccessTokenScopeWriteOrg, AccessTokenScopeReadOrg,
  91. AccessTokenScopeAdminPublicKey, AccessTokenScopeWritePublicKey, AccessTokenScopeReadPublicKey,
  92. AccessTokenScopeAdminRepoHook, AccessTokenScopeWriteRepoHook, AccessTokenScopeReadRepoHook,
  93. AccessTokenScopeAdminOrgHook,
  94. AccessTokenScopeNotification,
  95. AccessTokenScopeUser, AccessTokenScopeReadUser, AccessTokenScopeUserEmail, AccessTokenScopeUserFollow,
  96. AccessTokenScopeDeleteRepo,
  97. AccessTokenScopePackage, AccessTokenScopeWritePackage, AccessTokenScopeReadPackage, AccessTokenScopeDeletePackage,
  98. AccessTokenScopeAdminGPGKey, AccessTokenScopeWriteGPGKey, AccessTokenScopeReadGPGKey,
  99. AccessTokenScopeAdminApplication, AccessTokenScopeWriteApplication, AccessTokenScopeReadApplication,
  100. AccessTokenScopeSudo,
  101. }
  102. // allAccessTokenScopeBits contains all access token scopes.
  103. var allAccessTokenScopeBits = map[AccessTokenScope]AccessTokenScopeBitmap{
  104. AccessTokenScopeRepo: AccessTokenScopeRepoBits,
  105. AccessTokenScopeRepoStatus: AccessTokenScopeRepoStatusBits,
  106. AccessTokenScopePublicRepo: AccessTokenScopePublicRepoBits,
  107. AccessTokenScopeAdminOrg: AccessTokenScopeAdminOrgBits,
  108. AccessTokenScopeWriteOrg: AccessTokenScopeWriteOrgBits,
  109. AccessTokenScopeReadOrg: AccessTokenScopeReadOrgBits,
  110. AccessTokenScopeAdminPublicKey: AccessTokenScopeAdminPublicKeyBits,
  111. AccessTokenScopeWritePublicKey: AccessTokenScopeWritePublicKeyBits,
  112. AccessTokenScopeReadPublicKey: AccessTokenScopeReadPublicKeyBits,
  113. AccessTokenScopeAdminRepoHook: AccessTokenScopeAdminRepoHookBits,
  114. AccessTokenScopeWriteRepoHook: AccessTokenScopeWriteRepoHookBits,
  115. AccessTokenScopeReadRepoHook: AccessTokenScopeReadRepoHookBits,
  116. AccessTokenScopeAdminOrgHook: AccessTokenScopeAdminOrgHookBits,
  117. AccessTokenScopeNotification: AccessTokenScopeNotificationBits,
  118. AccessTokenScopeUser: AccessTokenScopeUserBits,
  119. AccessTokenScopeReadUser: AccessTokenScopeReadUserBits,
  120. AccessTokenScopeUserEmail: AccessTokenScopeUserEmailBits,
  121. AccessTokenScopeUserFollow: AccessTokenScopeUserFollowBits,
  122. AccessTokenScopeDeleteRepo: AccessTokenScopeDeleteRepoBits,
  123. AccessTokenScopePackage: AccessTokenScopePackageBits,
  124. AccessTokenScopeWritePackage: AccessTokenScopeWritePackageBits,
  125. AccessTokenScopeReadPackage: AccessTokenScopeReadPackageBits,
  126. AccessTokenScopeDeletePackage: AccessTokenScopeDeletePackageBits,
  127. AccessTokenScopeAdminGPGKey: AccessTokenScopeAdminGPGKeyBits,
  128. AccessTokenScopeWriteGPGKey: AccessTokenScopeWriteGPGKeyBits,
  129. AccessTokenScopeReadGPGKey: AccessTokenScopeReadGPGKeyBits,
  130. AccessTokenScopeAdminApplication: AccessTokenScopeAdminApplicationBits,
  131. AccessTokenScopeWriteApplication: AccessTokenScopeWriteApplicationBits,
  132. AccessTokenScopeReadApplication: AccessTokenScopeReadApplicationBits,
  133. AccessTokenScopeSudo: AccessTokenScopeSudoBits,
  134. }
  135. // Parse parses the scope string into a bitmap, thus removing possible duplicates.
  136. func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) {
  137. list := strings.Split(string(s), ",")
  138. var bitmap AccessTokenScopeBitmap
  139. for _, v := range list {
  140. singleScope := AccessTokenScope(v)
  141. if singleScope == "" {
  142. continue
  143. }
  144. if singleScope == AccessTokenScopeAll {
  145. bitmap |= AccessTokenScopeAllBits
  146. continue
  147. }
  148. bits, ok := allAccessTokenScopeBits[singleScope]
  149. if !ok {
  150. return 0, fmt.Errorf("invalid access token scope: %s", singleScope)
  151. }
  152. bitmap |= bits
  153. }
  154. return bitmap, nil
  155. }
  156. // Normalize returns a normalized scope string without any duplicates.
  157. func (s AccessTokenScope) Normalize() (AccessTokenScope, error) {
  158. bitmap, err := s.Parse()
  159. if err != nil {
  160. return "", err
  161. }
  162. return bitmap.ToScope(), nil
  163. }
  164. // HasScope returns true if the string has the given scope
  165. func (s AccessTokenScope) HasScope(scope AccessTokenScope) (bool, error) {
  166. bitmap, err := s.Parse()
  167. if err != nil {
  168. return false, err
  169. }
  170. return bitmap.HasScope(scope)
  171. }
  172. // HasScope returns true if the string has the given scope
  173. func (bitmap AccessTokenScopeBitmap) HasScope(scope AccessTokenScope) (bool, error) {
  174. expectedBits, ok := allAccessTokenScopeBits[scope]
  175. if !ok {
  176. return false, fmt.Errorf("invalid access token scope: %s", scope)
  177. }
  178. return bitmap&expectedBits == expectedBits, nil
  179. }
  180. // ToScope returns a normalized scope string without any duplicates.
  181. func (bitmap AccessTokenScopeBitmap) ToScope() AccessTokenScope {
  182. var scopes []string
  183. // iterate over all scopes, and reconstruct the bitmap
  184. // if the reconstructed bitmap doesn't change, then the scope is already included
  185. var reconstruct AccessTokenScopeBitmap
  186. for _, singleScope := range allAccessTokenScopes {
  187. // no need for error checking here, since we know the scope is valid
  188. if ok, _ := bitmap.HasScope(singleScope); ok {
  189. current := reconstruct | allAccessTokenScopeBits[singleScope]
  190. if current == reconstruct {
  191. continue
  192. }
  193. reconstruct = current
  194. scopes = append(scopes, string(singleScope))
  195. }
  196. }
  197. scope := AccessTokenScope(strings.Join(scopes, ","))
  198. scope = AccessTokenScope(strings.ReplaceAll(
  199. string(scope),
  200. "repo,admin:org,admin:public_key,admin:org_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application",
  201. "all",
  202. ))
  203. return scope
  204. }