feat: add licensing API token scope #698
@@ -3,6 +3,7 @@
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- API token scope `read:licensing` / `write:licensing` for licensing endpoints (#697)
|
||||
- Wiki full-text search: case-insensitive search across all wiki page titles and content (#550)
|
||||
- Wiki search API: GET /wiki/search?q=term with paginated JSON results (#550)
|
||||
- Metadata deploy fields: deploy_host, deploy_port, deploy_user, deploy_path, docker_image, docker_registry, container_name, health_url (#692)
|
||||
|
||||
@@ -24,6 +24,7 @@ const (
|
||||
AccessTokenScopeCategoryIssue
|
||||
AccessTokenScopeCategoryRepository
|
||||
AccessTokenScopeCategoryUser
|
||||
AccessTokenScopeCategoryLicensing
|
||||
)
|
||||
|
||||
// AllAccessTokenScopeCategories contains all access token scope categories
|
||||
@@ -37,6 +38,7 @@ var AllAccessTokenScopeCategories = []AccessTokenScopeCategory{
|
||||
AccessTokenScopeCategoryIssue,
|
||||
AccessTokenScopeCategoryRepository,
|
||||
AccessTokenScopeCategoryUser,
|
||||
AccessTokenScopeCategoryLicensing,
|
||||
}
|
||||
|
||||
// AccessTokenScopeLevel represents the access levels without a given scope category
|
||||
@@ -82,6 +84,9 @@ const (
|
||||
|
||||
AccessTokenScopeReadUser AccessTokenScope = "read:user"
|
||||
AccessTokenScopeWriteUser AccessTokenScope = "write:user"
|
||||
|
||||
AccessTokenScopeReadLicensing AccessTokenScope = "read:licensing"
|
||||
AccessTokenScopeWriteLicensing AccessTokenScope = "write:licensing"
|
||||
)
|
||||
|
||||
// accessTokenScopeBitmap represents a bitmap of access token scopes.
|
||||
@@ -93,7 +98,8 @@ const (
|
||||
accessTokenScopeAllBits accessTokenScopeBitmap = accessTokenScopeWriteActivityPubBits |
|
||||
accessTokenScopeWriteAdminBits | accessTokenScopeWriteMiscBits | accessTokenScopeWriteNotificationBits |
|
||||
accessTokenScopeWriteOrganizationBits | accessTokenScopeWritePackageBits | accessTokenScopeWriteIssueBits |
|
||||
accessTokenScopeWriteRepositoryBits | accessTokenScopeWriteUserBits
|
||||
accessTokenScopeWriteRepositoryBits | accessTokenScopeWriteUserBits |
|
||||
accessTokenScopeWriteLicensingBits
|
||||
|
||||
accessTokenScopePublicOnlyBits accessTokenScopeBitmap = 1 << iota
|
||||
|
||||
@@ -124,6 +130,9 @@ const (
|
||||
accessTokenScopeReadUserBits accessTokenScopeBitmap = 1 << iota
|
||||
accessTokenScopeWriteUserBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadUserBits
|
||||
|
||||
accessTokenScopeReadLicensingBits accessTokenScopeBitmap = 1 << iota
|
||||
accessTokenScopeWriteLicensingBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadLicensingBits
|
||||
|
||||
// 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.
|
||||
@@ -142,6 +151,7 @@ var allAccessTokenScopes = []AccessTokenScope{
|
||||
AccessTokenScopeWriteIssue, AccessTokenScopeReadIssue,
|
||||
AccessTokenScopeWriteRepository, AccessTokenScopeReadRepository,
|
||||
AccessTokenScopeWriteUser, AccessTokenScopeReadUser,
|
||||
AccessTokenScopeWriteLicensing, AccessTokenScopeReadLicensing,
|
||||
}
|
||||
|
||||
// allAccessTokenScopeBits contains all access token scopes.
|
||||
@@ -166,6 +176,8 @@ var allAccessTokenScopeBits = map[AccessTokenScope]accessTokenScopeBitmap{
|
||||
AccessTokenScopeWriteRepository: accessTokenScopeWriteRepositoryBits,
|
||||
AccessTokenScopeReadUser: accessTokenScopeReadUserBits,
|
||||
AccessTokenScopeWriteUser: accessTokenScopeWriteUserBits,
|
||||
AccessTokenScopeReadLicensing: accessTokenScopeReadLicensingBits,
|
||||
AccessTokenScopeWriteLicensing: accessTokenScopeWriteLicensingBits,
|
||||
}
|
||||
|
||||
// readAccessTokenScopes maps a scope category to the read permission scope
|
||||
@@ -180,6 +192,7 @@ var accessTokenScopes = map[AccessTokenScopeLevel]map[AccessTokenScopeCategory]A
|
||||
AccessTokenScopeCategoryIssue: AccessTokenScopeReadIssue,
|
||||
AccessTokenScopeCategoryRepository: AccessTokenScopeReadRepository,
|
||||
AccessTokenScopeCategoryUser: AccessTokenScopeReadUser,
|
||||
AccessTokenScopeCategoryLicensing: AccessTokenScopeReadLicensing,
|
||||
},
|
||||
Write: {
|
||||
AccessTokenScopeCategoryActivityPub: AccessTokenScopeWriteActivityPub,
|
||||
@@ -191,6 +204,7 @@ var accessTokenScopes = map[AccessTokenScopeLevel]map[AccessTokenScopeCategory]A
|
||||
AccessTokenScopeCategoryIssue: AccessTokenScopeWriteIssue,
|
||||
AccessTokenScopeCategoryRepository: AccessTokenScopeWriteRepository,
|
||||
AccessTokenScopeCategoryUser: AccessTokenScopeWriteUser,
|
||||
AccessTokenScopeCategoryLicensing: AccessTokenScopeWriteLicensing,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -370,7 +384,7 @@ func (bitmap accessTokenScopeBitmap) toScope() AccessTokenScope {
|
||||
scope := AccessTokenScope(strings.Join(scopes, ","))
|
||||
scope = AccessTokenScope(strings.ReplaceAll(
|
||||
string(scope),
|
||||
"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user",
|
||||
"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,write:licensing",
|
||||
"all",
|
||||
))
|
||||
return scope
|
||||
|
||||
@@ -17,13 +17,13 @@ type scopeTestNormalize struct {
|
||||
}
|
||||
|
||||
func TestAccessTokenScope_Normalize(t *testing.T) {
|
||||
assert.Equal(t, []string{"activitypub", "admin", "issue", "misc", "notification", "organization", "package", "repository", "user"}, GetAccessTokenCategories())
|
||||
assert.Equal(t, []string{"activitypub", "admin", "issue", "licensing", "misc", "notification", "organization", "package", "repository", "user"}, GetAccessTokenCategories())
|
||||
tests := []scopeTestNormalize{
|
||||
{"", "", nil},
|
||||
{"write:misc,write:notification,read:package,write:notification,public-only", "public-only,write:misc,write:notification,read:package", nil},
|
||||
{"all", "all", nil},
|
||||
{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user", "all", nil},
|
||||
{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,public-only", "public-only,all", nil},
|
||||
{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,write:licensing", "all", nil},
|
||||
{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,write:licensing,public-only", "public-only,all", nil},
|
||||
}
|
||||
|
||||
for _, scope := range GetAccessTokenCategories() {
|
||||
|
||||
@@ -294,6 +294,9 @@ func checkTokenPublicOnly() func(ctx *context.APIContext) {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public packages")
|
||||
return
|
||||
}
|
||||
case auth_model.AccessTokenScopeCategoryLicensing:
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public resources, licensing is not available")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1892,7 +1895,7 @@ func Routes() *web.Router {
|
||||
|
||||
// Authenticated license detail
|
||||
m.Get("/{dlid}/status", reqToken(), licensing.Status)
|
||||
})
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryLicensing))
|
||||
}, sudo())
|
||||
|
||||
return m
|
||||
|
||||
Reference in New Issue
Block a user