mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-30 19:08:37 +00:00 
			
		
		
		
	Currently we can add webhooks for organizations but not for users. This PR adds the latter. You can access it from the current users settings. 
		
			
				
	
	
		
			277 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			277 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2022 The Gitea Authors. All rights reserved.
 | |
| // SPDX-License-Identifier: MIT
 | |
| 
 | |
| package auth
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| // AccessTokenScope represents the scope for an access token.
 | |
| type AccessTokenScope string
 | |
| 
 | |
| const (
 | |
| 	AccessTokenScopeAll AccessTokenScope = "all"
 | |
| 
 | |
| 	AccessTokenScopeRepo       AccessTokenScope = "repo"
 | |
| 	AccessTokenScopeRepoStatus AccessTokenScope = "repo:status"
 | |
| 	AccessTokenScopePublicRepo AccessTokenScope = "public_repo"
 | |
| 
 | |
| 	AccessTokenScopeAdminOrg AccessTokenScope = "admin:org"
 | |
| 	AccessTokenScopeWriteOrg AccessTokenScope = "write:org"
 | |
| 	AccessTokenScopeReadOrg  AccessTokenScope = "read:org"
 | |
| 
 | |
| 	AccessTokenScopeAdminPublicKey AccessTokenScope = "admin:public_key"
 | |
| 	AccessTokenScopeWritePublicKey AccessTokenScope = "write:public_key"
 | |
| 	AccessTokenScopeReadPublicKey  AccessTokenScope = "read:public_key"
 | |
| 
 | |
| 	AccessTokenScopeAdminRepoHook AccessTokenScope = "admin:repo_hook"
 | |
| 	AccessTokenScopeWriteRepoHook AccessTokenScope = "write:repo_hook"
 | |
| 	AccessTokenScopeReadRepoHook  AccessTokenScope = "read:repo_hook"
 | |
| 
 | |
| 	AccessTokenScopeAdminOrgHook AccessTokenScope = "admin:org_hook"
 | |
| 
 | |
| 	AccessTokenScopeAdminUserHook AccessTokenScope = "admin:user_hook"
 | |
| 
 | |
| 	AccessTokenScopeNotification AccessTokenScope = "notification"
 | |
| 
 | |
| 	AccessTokenScopeUser       AccessTokenScope = "user"
 | |
| 	AccessTokenScopeReadUser   AccessTokenScope = "read:user"
 | |
| 	AccessTokenScopeUserEmail  AccessTokenScope = "user:email"
 | |
| 	AccessTokenScopeUserFollow AccessTokenScope = "user:follow"
 | |
| 
 | |
| 	AccessTokenScopeDeleteRepo AccessTokenScope = "delete_repo"
 | |
| 
 | |
| 	AccessTokenScopePackage       AccessTokenScope = "package"
 | |
| 	AccessTokenScopeWritePackage  AccessTokenScope = "write:package"
 | |
| 	AccessTokenScopeReadPackage   AccessTokenScope = "read:package"
 | |
| 	AccessTokenScopeDeletePackage AccessTokenScope = "delete:package"
 | |
| 
 | |
| 	AccessTokenScopeAdminGPGKey AccessTokenScope = "admin:gpg_key"
 | |
| 	AccessTokenScopeWriteGPGKey AccessTokenScope = "write:gpg_key"
 | |
| 	AccessTokenScopeReadGPGKey  AccessTokenScope = "read:gpg_key"
 | |
| 
 | |
| 	AccessTokenScopeAdminApplication AccessTokenScope = "admin:application"
 | |
| 	AccessTokenScopeWriteApplication AccessTokenScope = "write:application"
 | |
| 	AccessTokenScopeReadApplication  AccessTokenScope = "read:application"
 | |
| 
 | |
| 	AccessTokenScopeSudo AccessTokenScope = "sudo"
 | |
| )
 | |
| 
 | |
| // AccessTokenScopeBitmap represents a bitmap of access token scopes.
 | |
| type AccessTokenScopeBitmap uint64
 | |
| 
 | |
| // Bitmap of each scope, including the child scopes.
 | |
| const (
 | |
| 	// AccessTokenScopeAllBits is the bitmap of all access token scopes, except `sudo`.
 | |
| 	AccessTokenScopeAllBits AccessTokenScopeBitmap = AccessTokenScopeRepoBits |
 | |
| 		AccessTokenScopeAdminOrgBits | AccessTokenScopeAdminPublicKeyBits | AccessTokenScopeAdminOrgHookBits | AccessTokenScopeAdminUserHookBits |
 | |
| 		AccessTokenScopeNotificationBits | AccessTokenScopeUserBits | AccessTokenScopeDeleteRepoBits |
 | |
| 		AccessTokenScopePackageBits | AccessTokenScopeAdminGPGKeyBits | AccessTokenScopeAdminApplicationBits
 | |
| 
 | |
| 	AccessTokenScopeRepoBits       AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeRepoStatusBits | AccessTokenScopePublicRepoBits | AccessTokenScopeAdminRepoHookBits
 | |
| 	AccessTokenScopeRepoStatusBits AccessTokenScopeBitmap = 1 << iota
 | |
| 	AccessTokenScopePublicRepoBits AccessTokenScopeBitmap = 1 << iota
 | |
| 
 | |
| 	AccessTokenScopeAdminOrgBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteOrgBits
 | |
| 	AccessTokenScopeWriteOrgBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadOrgBits
 | |
| 	AccessTokenScopeReadOrgBits  AccessTokenScopeBitmap = 1 << iota
 | |
| 
 | |
| 	AccessTokenScopeAdminPublicKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWritePublicKeyBits
 | |
| 	AccessTokenScopeWritePublicKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadPublicKeyBits
 | |
| 	AccessTokenScopeReadPublicKeyBits  AccessTokenScopeBitmap = 1 << iota
 | |
| 
 | |
| 	AccessTokenScopeAdminRepoHookBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteRepoHookBits
 | |
| 	AccessTokenScopeWriteRepoHookBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadRepoHookBits
 | |
| 	AccessTokenScopeReadRepoHookBits  AccessTokenScopeBitmap = 1 << iota
 | |
| 
 | |
| 	AccessTokenScopeAdminOrgHookBits AccessTokenScopeBitmap = 1 << iota
 | |
| 
 | |
| 	AccessTokenScopeAdminUserHookBits AccessTokenScopeBitmap = 1 << iota
 | |
| 
 | |
| 	AccessTokenScopeNotificationBits AccessTokenScopeBitmap = 1 << iota
 | |
| 
 | |
| 	AccessTokenScopeUserBits       AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadUserBits | AccessTokenScopeUserEmailBits | AccessTokenScopeUserFollowBits
 | |
| 	AccessTokenScopeReadUserBits   AccessTokenScopeBitmap = 1 << iota
 | |
| 	AccessTokenScopeUserEmailBits  AccessTokenScopeBitmap = 1 << iota
 | |
| 	AccessTokenScopeUserFollowBits AccessTokenScopeBitmap = 1 << iota
 | |
| 
 | |
| 	AccessTokenScopeDeleteRepoBits AccessTokenScopeBitmap = 1 << iota
 | |
| 
 | |
| 	AccessTokenScopePackageBits       AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWritePackageBits | AccessTokenScopeDeletePackageBits
 | |
| 	AccessTokenScopeWritePackageBits  AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadPackageBits
 | |
| 	AccessTokenScopeReadPackageBits   AccessTokenScopeBitmap = 1 << iota
 | |
| 	AccessTokenScopeDeletePackageBits AccessTokenScopeBitmap = 1 << iota
 | |
| 
 | |
| 	AccessTokenScopeAdminGPGKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteGPGKeyBits
 | |
| 	AccessTokenScopeWriteGPGKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadGPGKeyBits
 | |
| 	AccessTokenScopeReadGPGKeyBits  AccessTokenScopeBitmap = 1 << iota
 | |
| 
 | |
| 	AccessTokenScopeAdminApplicationBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteApplicationBits
 | |
| 	AccessTokenScopeWriteApplicationBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadApplicationBits
 | |
| 	AccessTokenScopeReadApplicationBits  AccessTokenScopeBitmap = 1 << iota
 | |
| 
 | |
| 	AccessTokenScopeSudoBits AccessTokenScopeBitmap = 1 << iota
 | |
| 
 | |
| 	// The current implementation only supports up to 64 token scopes.
 | |
| 	// If we need to support > 64 scopes,
 | |
| 	// refactoring the whole implementation in this file (and only this file) is needed.
 | |
| )
 | |
| 
 | |
| // allAccessTokenScopes contains all access token scopes.
 | |
| // The order is important: parent scope must precedes child scopes.
 | |
| var allAccessTokenScopes = []AccessTokenScope{
 | |
| 	AccessTokenScopeRepo, AccessTokenScopeRepoStatus, AccessTokenScopePublicRepo,
 | |
| 	AccessTokenScopeAdminOrg, AccessTokenScopeWriteOrg, AccessTokenScopeReadOrg,
 | |
| 	AccessTokenScopeAdminPublicKey, AccessTokenScopeWritePublicKey, AccessTokenScopeReadPublicKey,
 | |
| 	AccessTokenScopeAdminRepoHook, AccessTokenScopeWriteRepoHook, AccessTokenScopeReadRepoHook,
 | |
| 	AccessTokenScopeAdminOrgHook,
 | |
| 	AccessTokenScopeAdminUserHook,
 | |
| 	AccessTokenScopeNotification,
 | |
| 	AccessTokenScopeUser, AccessTokenScopeReadUser, AccessTokenScopeUserEmail, AccessTokenScopeUserFollow,
 | |
| 	AccessTokenScopeDeleteRepo,
 | |
| 	AccessTokenScopePackage, AccessTokenScopeWritePackage, AccessTokenScopeReadPackage, AccessTokenScopeDeletePackage,
 | |
| 	AccessTokenScopeAdminGPGKey, AccessTokenScopeWriteGPGKey, AccessTokenScopeReadGPGKey,
 | |
| 	AccessTokenScopeAdminApplication, AccessTokenScopeWriteApplication, AccessTokenScopeReadApplication,
 | |
| 	AccessTokenScopeSudo,
 | |
| }
 | |
| 
 | |
| // allAccessTokenScopeBits contains all access token scopes.
 | |
| var allAccessTokenScopeBits = map[AccessTokenScope]AccessTokenScopeBitmap{
 | |
| 	AccessTokenScopeRepo:             AccessTokenScopeRepoBits,
 | |
| 	AccessTokenScopeRepoStatus:       AccessTokenScopeRepoStatusBits,
 | |
| 	AccessTokenScopePublicRepo:       AccessTokenScopePublicRepoBits,
 | |
| 	AccessTokenScopeAdminOrg:         AccessTokenScopeAdminOrgBits,
 | |
| 	AccessTokenScopeWriteOrg:         AccessTokenScopeWriteOrgBits,
 | |
| 	AccessTokenScopeReadOrg:          AccessTokenScopeReadOrgBits,
 | |
| 	AccessTokenScopeAdminPublicKey:   AccessTokenScopeAdminPublicKeyBits,
 | |
| 	AccessTokenScopeWritePublicKey:   AccessTokenScopeWritePublicKeyBits,
 | |
| 	AccessTokenScopeReadPublicKey:    AccessTokenScopeReadPublicKeyBits,
 | |
| 	AccessTokenScopeAdminRepoHook:    AccessTokenScopeAdminRepoHookBits,
 | |
| 	AccessTokenScopeWriteRepoHook:    AccessTokenScopeWriteRepoHookBits,
 | |
| 	AccessTokenScopeReadRepoHook:     AccessTokenScopeReadRepoHookBits,
 | |
| 	AccessTokenScopeAdminOrgHook:     AccessTokenScopeAdminOrgHookBits,
 | |
| 	AccessTokenScopeAdminUserHook:    AccessTokenScopeAdminUserHookBits,
 | |
| 	AccessTokenScopeNotification:     AccessTokenScopeNotificationBits,
 | |
| 	AccessTokenScopeUser:             AccessTokenScopeUserBits,
 | |
| 	AccessTokenScopeReadUser:         AccessTokenScopeReadUserBits,
 | |
| 	AccessTokenScopeUserEmail:        AccessTokenScopeUserEmailBits,
 | |
| 	AccessTokenScopeUserFollow:       AccessTokenScopeUserFollowBits,
 | |
| 	AccessTokenScopeDeleteRepo:       AccessTokenScopeDeleteRepoBits,
 | |
| 	AccessTokenScopePackage:          AccessTokenScopePackageBits,
 | |
| 	AccessTokenScopeWritePackage:     AccessTokenScopeWritePackageBits,
 | |
| 	AccessTokenScopeReadPackage:      AccessTokenScopeReadPackageBits,
 | |
| 	AccessTokenScopeDeletePackage:    AccessTokenScopeDeletePackageBits,
 | |
| 	AccessTokenScopeAdminGPGKey:      AccessTokenScopeAdminGPGKeyBits,
 | |
| 	AccessTokenScopeWriteGPGKey:      AccessTokenScopeWriteGPGKeyBits,
 | |
| 	AccessTokenScopeReadGPGKey:       AccessTokenScopeReadGPGKeyBits,
 | |
| 	AccessTokenScopeAdminApplication: AccessTokenScopeAdminApplicationBits,
 | |
| 	AccessTokenScopeWriteApplication: AccessTokenScopeWriteApplicationBits,
 | |
| 	AccessTokenScopeReadApplication:  AccessTokenScopeReadApplicationBits,
 | |
| 	AccessTokenScopeSudo:             AccessTokenScopeSudoBits,
 | |
| }
 | |
| 
 | |
| // Parse parses the scope string into a bitmap, thus removing possible duplicates.
 | |
| func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) {
 | |
| 	var bitmap AccessTokenScopeBitmap
 | |
| 
 | |
| 	// The following is the more performant equivalent of 'for _, v := range strings.Split(remainingScope, ",")' as this is hot code
 | |
| 	remainingScopes := string(s)
 | |
| 	for len(remainingScopes) > 0 {
 | |
| 		i := strings.IndexByte(remainingScopes, ',')
 | |
| 		var v string
 | |
| 		if i < 0 {
 | |
| 			v = remainingScopes
 | |
| 			remainingScopes = ""
 | |
| 		} else if i+1 >= len(remainingScopes) {
 | |
| 			v = remainingScopes[:i]
 | |
| 			remainingScopes = ""
 | |
| 		} else {
 | |
| 			v = remainingScopes[:i]
 | |
| 			remainingScopes = remainingScopes[i+1:]
 | |
| 		}
 | |
| 		singleScope := AccessTokenScope(v)
 | |
| 		if singleScope == "" {
 | |
| 			continue
 | |
| 		}
 | |
| 		if singleScope == AccessTokenScopeAll {
 | |
| 			bitmap |= AccessTokenScopeAllBits
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		bits, ok := allAccessTokenScopeBits[singleScope]
 | |
| 		if !ok {
 | |
| 			return 0, fmt.Errorf("invalid access token scope: %s", singleScope)
 | |
| 		}
 | |
| 		bitmap |= bits
 | |
| 	}
 | |
| 
 | |
| 	return bitmap, nil
 | |
| }
 | |
| 
 | |
| // StringSlice returns the AccessTokenScope as a []string
 | |
| func (s AccessTokenScope) StringSlice() []string {
 | |
| 	return strings.Split(string(s), ",")
 | |
| }
 | |
| 
 | |
| // Normalize returns a normalized scope string without any duplicates.
 | |
| func (s AccessTokenScope) Normalize() (AccessTokenScope, error) {
 | |
| 	bitmap, err := s.Parse()
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 
 | |
| 	return bitmap.ToScope(), nil
 | |
| }
 | |
| 
 | |
| // HasScope returns true if the string has the given scope
 | |
| func (s AccessTokenScope) HasScope(scope AccessTokenScope) (bool, error) {
 | |
| 	bitmap, err := s.Parse()
 | |
| 	if err != nil {
 | |
| 		return false, err
 | |
| 	}
 | |
| 
 | |
| 	return bitmap.HasScope(scope)
 | |
| }
 | |
| 
 | |
| // HasScope returns true if the string has the given scope
 | |
| func (bitmap AccessTokenScopeBitmap) HasScope(scope AccessTokenScope) (bool, error) {
 | |
| 	expectedBits, ok := allAccessTokenScopeBits[scope]
 | |
| 	if !ok {
 | |
| 		return false, fmt.Errorf("invalid access token scope: %s", scope)
 | |
| 	}
 | |
| 
 | |
| 	return bitmap&expectedBits == expectedBits, nil
 | |
| }
 | |
| 
 | |
| // ToScope returns a normalized scope string without any duplicates.
 | |
| func (bitmap AccessTokenScopeBitmap) ToScope() AccessTokenScope {
 | |
| 	var scopes []string
 | |
| 
 | |
| 	// iterate over all scopes, and reconstruct the bitmap
 | |
| 	// if the reconstructed bitmap doesn't change, then the scope is already included
 | |
| 	var reconstruct AccessTokenScopeBitmap
 | |
| 
 | |
| 	for _, singleScope := range allAccessTokenScopes {
 | |
| 		// no need for error checking here, since we know the scope is valid
 | |
| 		if ok, _ := bitmap.HasScope(singleScope); ok {
 | |
| 			current := reconstruct | allAccessTokenScopeBits[singleScope]
 | |
| 			if current == reconstruct {
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			reconstruct = current
 | |
| 			scopes = append(scopes, string(singleScope))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	scope := AccessTokenScope(strings.Join(scopes, ","))
 | |
| 	scope = AccessTokenScope(strings.ReplaceAll(
 | |
| 		string(scope),
 | |
| 		"repo,admin:org,admin:public_key,admin:org_hook,admin:user_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application",
 | |
| 		"all",
 | |
| 	))
 | |
| 	return scope
 | |
| }
 |