@ -235,6 +235,62 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext)
}
}
}
}
func checkTokenPublicOnly ( ) func ( ctx * context . APIContext ) {
return func ( ctx * context . APIContext ) {
if ! ctx . PublicOnly {
return
}
requiredScopeCategories , ok := ctx . Data [ "requiredScopeCategories" ] . ( [ ] auth_model . AccessTokenScopeCategory )
if ! ok || len ( requiredScopeCategories ) == 0 {
return
}
// public Only permission check
switch {
case auth_model . ContainsCategory ( requiredScopeCategories , auth_model . AccessTokenScopeCategoryRepository ) :
if ctx . Repo . Repository != nil && ctx . Repo . Repository . IsPrivate {
ctx . Error ( http . StatusForbidden , "reqToken" , "token scope is limited to public repos" )
return
}
case auth_model . ContainsCategory ( requiredScopeCategories , auth_model . AccessTokenScopeCategoryIssue ) :
if ctx . Repo . Repository != nil && ctx . Repo . Repository . IsPrivate {
ctx . Error ( http . StatusForbidden , "reqToken" , "token scope is limited to public issues" )
return
}
case auth_model . ContainsCategory ( requiredScopeCategories , auth_model . AccessTokenScopeCategoryOrganization ) :
if ctx . Org . Organization != nil && ctx . Org . Organization . Visibility != api . VisibleTypePublic {
ctx . Error ( http . StatusForbidden , "reqToken" , "token scope is limited to public orgs" )
return
}
if ctx . ContextUser != nil && ctx . ContextUser . IsOrganization ( ) && ctx . ContextUser . Visibility != api . VisibleTypePublic {
ctx . Error ( http . StatusForbidden , "reqToken" , "token scope is limited to public orgs" )
return
}
case auth_model . ContainsCategory ( requiredScopeCategories , auth_model . AccessTokenScopeCategoryUser ) :
if ctx . ContextUser != nil && ctx . ContextUser . IsUser ( ) && ctx . ContextUser . Visibility != api . VisibleTypePublic {
ctx . Error ( http . StatusForbidden , "reqToken" , "token scope is limited to public users" )
return
}
case auth_model . ContainsCategory ( requiredScopeCategories , auth_model . AccessTokenScopeCategoryActivityPub ) :
if ctx . ContextUser != nil && ctx . ContextUser . IsUser ( ) && ctx . ContextUser . Visibility != api . VisibleTypePublic {
ctx . Error ( http . StatusForbidden , "reqToken" , "token scope is limited to public activitypub" )
return
}
case auth_model . ContainsCategory ( requiredScopeCategories , auth_model . AccessTokenScopeCategoryNotification ) :
if ctx . Repo . Repository != nil && ctx . Repo . Repository . IsPrivate {
ctx . Error ( http . StatusForbidden , "reqToken" , "token scope is limited to public notifications" )
return
}
case auth_model . ContainsCategory ( requiredScopeCategories , auth_model . AccessTokenScopeCategoryPackage ) :
if ctx . Package != nil && ctx . Package . Owner . Visibility . IsPrivate ( ) {
ctx . Error ( http . StatusForbidden , "reqToken" , "token scope is limited to public packages" )
return
}
}
}
}
// if a token is being used for auth, we check that it contains the required scope
// if a token is being used for auth, we check that it contains the required scope
// if a token is not being used, reqToken will enforce other sign in methods
// if a token is not being used, reqToken will enforce other sign in methods
func tokenRequiresScopes ( requiredScopeCategories ... auth_model . AccessTokenScopeCategory ) func ( ctx * context . APIContext ) {
func tokenRequiresScopes ( requiredScopeCategories ... auth_model . AccessTokenScopeCategory ) func ( ctx * context . APIContext ) {
@ -250,9 +306,6 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC
return
return
}
}
ctx . Data [ "ApiTokenScopePublicRepoOnly" ] = false
ctx . Data [ "ApiTokenScopePublicOrgOnly" ] = false
// use the http method to determine the access level
// use the http method to determine the access level
requiredScopeLevel := auth_model . Read
requiredScopeLevel := auth_model . Read
if ctx . Req . Method == "POST" || ctx . Req . Method == "PUT" || ctx . Req . Method == "PATCH" || ctx . Req . Method == "DELETE" {
if ctx . Req . Method == "POST" || ctx . Req . Method == "PUT" || ctx . Req . Method == "PATCH" || ctx . Req . Method == "DELETE" {
@ -261,29 +314,28 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC
// get the required scope for the given access level and category
// get the required scope for the given access level and category
requiredScopes := auth_model . GetRequiredScopes ( requiredScopeLevel , requiredScopeCategories ... )
requiredScopes := auth_model . GetRequiredScopes ( requiredScopeLevel , requiredScopeCategories ... )
allow , err := scope . HasScope ( requiredScopes ... )
// check if scope only applies to public resources
publicOnly , err := scope . PublicOnly ( )
if err != nil {
if err != nil {
ctx . Error ( http . StatusForbidden , "tokenRequiresScope" , "parsing public resource scope failed: " + err . Error ( ) )
ctx . Error ( http . StatusForbidden , "tokenRequiresScope" , "checking scope failed: " + err . Error ( ) )
return
return
}
}
// this context is used by the middleware in the specific route
if ! allow {
ctx . Data [ "ApiTokenScopePublicRepoOnly" ] = publicOnly && auth_model . ContainsCategory ( requiredScopeCategories , auth_model . AccessTokenScopeCategoryRepository )
ctx . Error ( http . StatusForbidden , "tokenRequiresScope" , fmt . Sprintf ( "token does not have at least one of required scope(s): %v" , requiredScopes ) )
ctx . Data [ "ApiTokenScopePublicOrgOnly" ] = publicOnly && auth_model . ContainsCategory ( requiredScopeCategories , auth_model . AccessTokenScopeCategoryOrganization )
allow , err := scope . HasScope ( requiredScopes ... )
if err != nil {
ctx . Error ( http . StatusForbidden , "tokenRequiresScope" , "checking scope failed: " + err . Error ( ) )
return
return
}
}
if allow {
ctx . Data [ "requiredScopeCategories" ] = requiredScopeCategories
// check if scope only applies to public resources
publicOnly , err := scope . PublicOnly ( )
if err != nil {
ctx . Error ( http . StatusForbidden , "tokenRequiresScope" , "parsing public resource scope failed: " + err . Error ( ) )
return
return
}
}
ctx . Error ( http . StatusForbidden , "tokenRequiresScope" , fmt . Sprintf ( "token does not have at least one of required scope(s): %v" , requiredScopes ) )
// assign to true so that those searching should only filter public repositories/users/organizations
ctx . PublicOnly = publicOnly
}
}
}
}
@ -295,25 +347,6 @@ func reqToken() func(ctx *context.APIContext) {
return
return
}
}
if true == ctx . Data [ "IsApiToken" ] {
publicRepo , pubRepoExists := ctx . Data [ "ApiTokenScopePublicRepoOnly" ]
publicOrg , pubOrgExists := ctx . Data [ "ApiTokenScopePublicOrgOnly" ]
if pubRepoExists && publicRepo . ( bool ) &&
ctx . Repo . Repository != nil && ctx . Repo . Repository . IsPrivate {
ctx . Error ( http . StatusForbidden , "reqToken" , "token scope is limited to public repos" )
return
}
if pubOrgExists && publicOrg . ( bool ) &&
ctx . Org . Organization != nil && ctx . Org . Organization . Visibility != api . VisibleTypePublic {
ctx . Error ( http . StatusForbidden , "reqToken" , "token scope is limited to public orgs" )
return
}
return
}
if ctx . IsSigned {
if ctx . IsSigned {
return
return
}
}
@ -879,11 +912,11 @@ func Routes() *web.Router {
m . Group ( "/user/{username}" , func ( ) {
m . Group ( "/user/{username}" , func ( ) {
m . Get ( "" , activitypub . Person )
m . Get ( "" , activitypub . Person )
m . Post ( "/inbox" , activitypub . ReqHTTPSignature ( ) , activitypub . PersonInbox )
m . Post ( "/inbox" , activitypub . ReqHTTPSignature ( ) , activitypub . PersonInbox )
} , context . UserAssignmentAPI ( ) )
} , context . UserAssignmentAPI ( ) , checkTokenPublicOnly ( ) )
m . Group ( "/user-id/{user-id}" , func ( ) {
m . Group ( "/user-id/{user-id}" , func ( ) {
m . Get ( "" , activitypub . Person )
m . Get ( "" , activitypub . Person )
m . Post ( "/inbox" , activitypub . ReqHTTPSignature ( ) , activitypub . PersonInbox )
m . Post ( "/inbox" , activitypub . ReqHTTPSignature ( ) , activitypub . PersonInbox )
} , context . UserIDAssignmentAPI ( ) )
} , context . UserIDAssignmentAPI ( ) , checkTokenPublicOnly ( ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryActivityPub ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryActivityPub ) )
}
}
@ -939,7 +972,7 @@ func Routes() *web.Router {
} , reqSelfOrAdmin ( ) , reqBasicOrRevProxyAuth ( ) )
} , reqSelfOrAdmin ( ) , reqBasicOrRevProxyAuth ( ) )
m . Get ( "/activities/feeds" , user . ListUserActivityFeeds )
m . Get ( "/activities/feeds" , user . ListUserActivityFeeds )
} , context . UserAssignmentAPI ( ) , individualPermsChecker )
} , context . UserAssignmentAPI ( ) , checkTokenPublicOnly ( ) , individualPermsChecker )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryUser ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryUser ) )
// Users (requires user scope)
// Users (requires user scope)
@ -957,7 +990,7 @@ func Routes() *web.Router {
m . Get ( "/starred" , user . GetStarredRepos )
m . Get ( "/starred" , user . GetStarredRepos )
m . Get ( "/subscriptions" , user . GetWatchedRepos )
m . Get ( "/subscriptions" , user . GetWatchedRepos )
} , context . UserAssignmentAPI ( ) )
} , context . UserAssignmentAPI ( ) , checkTokenPublicOnly ( ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryUser ) , reqToken ( ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryUser ) , reqToken ( ) )
// Users (requires user scope)
// Users (requires user scope)
@ -1044,7 +1077,7 @@ func Routes() *web.Router {
m . Get ( "" , user . IsStarring )
m . Get ( "" , user . IsStarring )
m . Put ( "" , user . Star )
m . Put ( "" , user . Star )
m . Delete ( "" , user . Unstar )
m . Delete ( "" , user . Unstar )
} , repoAssignment ( ) )
} , repoAssignment ( ) , checkTokenPublicOnly ( ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryRepository ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryRepository ) )
m . Get ( "/times" , repo . ListMyTrackedTimes )
m . Get ( "/times" , repo . ListMyTrackedTimes )
m . Get ( "/stopwatches" , repo . GetStopwatches )
m . Get ( "/stopwatches" , repo . GetStopwatches )
@ -1069,18 +1102,20 @@ func Routes() *web.Router {
m . Get ( "" , user . CheckUserBlock )
m . Get ( "" , user . CheckUserBlock )
m . Put ( "" , user . BlockUser )
m . Put ( "" , user . BlockUser )
m . Delete ( "" , user . UnblockUser )
m . Delete ( "" , user . UnblockUser )
} , context . UserAssignmentAPI ( ) )
} , context . UserAssignmentAPI ( ) , checkTokenPublicOnly ( ) )
} )
} )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryUser ) , reqToken ( ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryUser ) , reqToken ( ) )
// Repositories (requires repo scope, org scope)
// Repositories (requires repo scope, org scope)
m . Post ( "/org/{org}/repos" ,
m . Post ( "/org/{org}/repos" ,
// FIXME: we need org in context
tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryOrganization , auth_model . AccessTokenScopeCategoryRepository ) ,
tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryOrganization , auth_model . AccessTokenScopeCategoryRepository ) ,
reqToken ( ) ,
reqToken ( ) ,
bind ( api . CreateRepoOption { } ) ,
bind ( api . CreateRepoOption { } ) ,
repo . CreateOrgRepoDeprecated )
repo . CreateOrgRepoDeprecated )
// requires repo scope
// requires repo scope
// FIXME: Don't expose repository id outside of the system
m . Combo ( "/repositories/{id}" , reqToken ( ) , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryRepository ) ) . Get ( repo . GetByID )
m . Combo ( "/repositories/{id}" , reqToken ( ) , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryRepository ) ) . Get ( repo . GetByID )
// Repos (requires repo scope)
// Repos (requires repo scope)
@ -1334,7 +1369,7 @@ func Routes() *web.Router {
m . Post ( "" , bind ( api . UpdateRepoAvatarOption { } ) , repo . UpdateAvatar )
m . Post ( "" , bind ( api . UpdateRepoAvatarOption { } ) , repo . UpdateAvatar )
m . Delete ( "" , repo . DeleteAvatar )
m . Delete ( "" , repo . DeleteAvatar )
} , reqAdmin ( ) , reqToken ( ) )
} , reqAdmin ( ) , reqToken ( ) )
} , repoAssignment ( ) )
} , repoAssignment ( ) , checkTokenPublicOnly ( ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryRepository ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryRepository ) )
// Notifications (requires notifications scope)
// Notifications (requires notifications scope)
@ -1343,7 +1378,7 @@ func Routes() *web.Router {
m . Combo ( "/notifications" , reqToken ( ) ) .
m . Combo ( "/notifications" , reqToken ( ) ) .
Get ( notify . ListRepoNotifications ) .
Get ( notify . ListRepoNotifications ) .
Put ( notify . ReadRepoNotifications )
Put ( notify . ReadRepoNotifications )
} , repoAssignment ( ) )
} , repoAssignment ( ) , checkTokenPublicOnly ( ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryNotification ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryNotification ) )
// Issue (requires issue scope)
// Issue (requires issue scope)
@ -1457,7 +1492,7 @@ func Routes() *web.Router {
Patch ( reqToken ( ) , reqRepoWriter ( unit . TypeIssues , unit . TypePullRequests ) , bind ( api . EditMilestoneOption { } ) , repo . EditMilestone ) .
Patch ( reqToken ( ) , reqRepoWriter ( unit . TypeIssues , unit . TypePullRequests ) , bind ( api . EditMilestoneOption { } ) , repo . EditMilestone ) .
Delete ( reqToken ( ) , reqRepoWriter ( unit . TypeIssues , unit . TypePullRequests ) , repo . DeleteMilestone )
Delete ( reqToken ( ) , reqRepoWriter ( unit . TypeIssues , unit . TypePullRequests ) , repo . DeleteMilestone )
} )
} )
} , repoAssignment ( ) )
} , repoAssignment ( ) , checkTokenPublicOnly ( ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryIssue ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryIssue ) )
// NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs
// NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs
@ -1468,14 +1503,14 @@ func Routes() *web.Router {
m . Get ( "/files" , reqToken ( ) , packages . ListPackageFiles )
m . Get ( "/files" , reqToken ( ) , packages . ListPackageFiles )
} )
} )
m . Get ( "/" , reqToken ( ) , packages . ListPackages )
m . Get ( "/" , reqToken ( ) , packages . ListPackages )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryPackage ) , context . UserAssignmentAPI ( ) , context . PackageAssignmentAPI ( ) , reqPackageAccess ( perm . AccessModeRead ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryPackage ) , context . UserAssignmentAPI ( ) , context . PackageAssignmentAPI ( ) , reqPackageAccess ( perm . AccessModeRead ) , checkTokenPublicOnly ( ) )
// Organizations
// Organizations
m . Get ( "/user/orgs" , reqToken ( ) , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryUser , auth_model . AccessTokenScopeCategoryOrganization ) , org . ListMyOrgs )
m . Get ( "/user/orgs" , reqToken ( ) , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryUser , auth_model . AccessTokenScopeCategoryOrganization ) , org . ListMyOrgs )
m . Group ( "/users/{username}/orgs" , func ( ) {
m . Group ( "/users/{username}/orgs" , func ( ) {
m . Get ( "" , reqToken ( ) , org . ListUserOrgs )
m . Get ( "" , reqToken ( ) , org . ListUserOrgs )
m . Get ( "/{org}/permissions" , reqToken ( ) , org . GetUserOrgsPermissions )
m . Get ( "/{org}/permissions" , reqToken ( ) , org . GetUserOrgsPermissions )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryUser , auth_model . AccessTokenScopeCategoryOrganization ) , context . UserAssignmentAPI ( ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryUser , auth_model . AccessTokenScopeCategoryOrganization ) , context . UserAssignmentAPI ( ) , checkTokenPublicOnly ( ) )
m . Post ( "/orgs" , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryOrganization ) , reqToken ( ) , bind ( api . CreateOrgOption { } ) , org . Create )
m . Post ( "/orgs" , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryOrganization ) , reqToken ( ) , bind ( api . CreateOrgOption { } ) , org . Create )
m . Get ( "/orgs" , org . GetAll , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryOrganization ) )
m . Get ( "/orgs" , org . GetAll , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryOrganization ) )
m . Group ( "/orgs/{org}" , func ( ) {
m . Group ( "/orgs/{org}" , func ( ) {
@ -1533,7 +1568,7 @@ func Routes() *web.Router {
m . Delete ( "" , org . UnblockUser )
m . Delete ( "" , org . UnblockUser )
} )
} )
} , reqToken ( ) , reqOrgOwnership ( ) )
} , reqToken ( ) , reqOrgOwnership ( ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryOrganization ) , orgAssignment ( true ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryOrganization ) , orgAssignment ( true ) , checkTokenPublicOnly ( ) )
m . Group ( "/teams/{teamid}" , func ( ) {
m . Group ( "/teams/{teamid}" , func ( ) {
m . Combo ( "" ) . Get ( reqToken ( ) , org . GetTeam ) .
m . Combo ( "" ) . Get ( reqToken ( ) , org . GetTeam ) .
Patch ( reqToken ( ) , reqOrgOwnership ( ) , bind ( api . EditTeamOption { } ) , org . EditTeam ) .
Patch ( reqToken ( ) , reqOrgOwnership ( ) , bind ( api . EditTeamOption { } ) , org . EditTeam ) .
@ -1553,7 +1588,7 @@ func Routes() *web.Router {
Get ( reqToken ( ) , org . GetTeamRepo )
Get ( reqToken ( ) , org . GetTeamRepo )
} )
} )
m . Get ( "/activities/feeds" , org . ListTeamActivityFeeds )
m . Get ( "/activities/feeds" , org . ListTeamActivityFeeds )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryOrganization ) , orgAssignment ( false , true ) , reqToken ( ) , reqTeamMembership ( ) )
} , tokenRequiresScopes ( auth_model . AccessTokenScopeCategoryOrganization ) , orgAssignment ( false , true ) , reqToken ( ) , reqTeamMembership ( ) , checkTokenPublicOnly ( ) )
m . Group ( "/admin" , func ( ) {
m . Group ( "/admin" , func ( ) {
m . Group ( "/cron" , func ( ) {
m . Group ( "/cron" , func ( ) {