diff --git a/CHANGELOG.md b/CHANGELOG.md index e82430e2ce5b..1e31b0008ad7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,47 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log; to see the highlights of what has been added to each release, please refer to the [blog](https://blog.gitea.io). +## [1.18.1](https://github.com/go-gitea/gitea/releases/tag/v1.18.1) - 2023-01-17 + +* API + * Add `sync_on_commit` option for push mirrors api (#22271) (#22292) +* BUGFIXES + * Update `github.com/zeripath/zapx/v15` (#22485) + * Fix pull request API field `closed_at` always being `null` (#22482) (#22483) + * Fix container blob mount (#22226) (#22476) + * Fix error when calculating repository size (#22392) (#22474) + * Fix Operator does not exist bug on explore page with ONLY_SHOW_RELEVANT_REPOS (#22454) (#22472) + * Fix environments for KaTeX and error reporting (#22453) (#22473) + * Remove the netgo tag for Windows build (#22467) (#22468) + * Fix migration from GitBucket (#22477) (#22465) + * Prevent panic on looking at api "git" endpoints for empty repos (#22457) (#22458) + * Fix PR status layout on mobile (#21547) (#22441) + * Fix wechatwork webhook sends empty content in PR review (#21762) (#22440) + * Remove duplicate "Actions" label in mobile view (#21974) (#22439) + * Fix leaving organization bug on user settings -> orgs (#21983) (#22438) + * Fixed colour transparency regex matching in project board sorting (#22092) (#22437) + * Correctly handle select on multiple channels in Queues (#22146) (#22428) + * Prepend refs/heads/ to issue template refs (#20461) (#22427) + * Restore function to "Show more" buttons (#22399) (#22426) + * Continue GCing other repos on error in one repo (#22422) (#22425) + * Allow HOST has no port (#22280) (#22409) + * Fix omit avatar_url in discord payload when empty (#22393) (#22394) + * Don't display stop watch top bar icon when disabled and hidden when click other place (#22374) (#22387) + * Don't lookup mail server when using sendmail (#22300) (#22383) + * Fix gravatar disable bug (#22337) + * Fix update settings table on install (#22326) (#22327) + * Fix sitemap (#22272) (#22320) + * Fix code search title translation (#22285) (#22316) + * Fix due date rendering the wrong date in issue (#22302) (#22306) + * Fix get system setting bug when enabled redis cache (#22298) + * Fix bug of DisableGravatar default value (#22297) + * Fix key signature error page (#22229) (#22230) +* TESTING + * Remove test session cache to reduce possible concurrent problem (#22199) (#22429) +* MISC + * Restore previous official review when an official review is deleted (#22449) (#22460) + * Log STDERR of external renderer when it fails (#22442) (#22444) + ## [1.18.0](https://github.com/go-gitea/gitea/releases/tag/v1.18.0) - 2022-12-29 * SECURITY diff --git a/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet b/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet index 3e2513c4cfca..31b7d4f9b2e8 100644 --- a/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet +++ b/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet @@ -29,7 +29,7 @@ local addIssueLabelsOverrides(labels) = grafanaDashboards+:: { - local giteaSelector = 'job="$job", instance="$instance"', + local giteaSelector = 'job=~"$job", instance=~"$instance"', local giteaStatsPanel = grafana.statPanel.new( 'Gitea stats', @@ -399,25 +399,31 @@ local addIssueLabelsOverrides(labels) = .addTemplate( { hide: 0, - label: null, + label: 'job', name: 'job', options: [], + datasource: '$datasource', query: 'label_values(gitea_organizations, job)', refresh: 1, regex: '', type: 'query', + multi: true, + allValue: '.+' }, ) .addTemplate( { hide: 0, - label: null, + label: 'instance', name: 'instance', options: [], + datasource: '$datasource', query: 'label_values(gitea_organizations{job="$job"}, instance)', refresh: 1, regex: '', type: 'query', + multi: true, + allValue: '.+' }, ) .addTemplate( diff --git a/docs/config.yaml b/docs/config.yaml index fbd7d8d81004..0b47c0ffd83f 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -18,7 +18,7 @@ params: description: Git with a cup of tea author: The Gitea Authors website: https://docs.gitea.io - version: 1.18.0 + version: 1.18.1 minGoVersion: 1.18 goVersion: 1.19 minNodeVersion: 16 diff --git a/docs/content/doc/developers/oauth2-provider.en-us.md b/docs/content/doc/developers/oauth2-provider.en-us.md index c6765f19e7aa..17c12d22f242 100644 --- a/docs/content/doc/developers/oauth2-provider.en-us.md +++ b/docs/content/doc/developers/oauth2-provider.en-us.md @@ -42,7 +42,41 @@ To use the Authorization Code Grant as a third party application it is required ## Scopes -Currently Gitea does not support scopes (see [#4300](https://github.com/go-gitea/gitea/issues/4300)) and all third party applications will be granted access to all resources of the user and their organizations. +Gitea supports the following scopes for tokens: + +| Name | Description | +| ---- | ----------- | +| **(no scope)** | Grants read-only access to public user profile and public repositories. | +| **repo** | Full control over all repositories. | +|     **repo:status** | Grants read/write access to commit status in all repositories. | +|     **public_repo** | Grants read/write access to public repositories only. | +| **admin:repo_hook** | Grants access to repository hooks of all repositories. This is included in the `repo` scope. | +|     **write:repo_hook** | Grants read/write access to repository hooks | +|     **read:repo_hook** | Grants read-only access to repository hooks | +| **admin:org** | Grants full access to organization settings | +|     **write:org** | Grants read/write access to organization settings | +|     **read:org** | Grants read-only access to organization settings | +| **admin:public_key** | Grants full access for managing public keys | +|     **write:public_key** | Grant read/write access to public keys | +|     **read:public_key** | Grant read-only access to public keys | +| **admin:org_hook** | Grants full access to organizational-level hooks | +| **notification** | Grants full access to notifications | +| **user** | Grants full access to user profile info | +|     **read:user** | Grants read access to user's profile | +|     **user:email** | Grants read access to user's email addresses | +|     **user:follow** | Grants access to follow/un-follow a user | +| **delete_repo** | Grants access to delete repositories as an admin | +| **package** | Grants full access to hosted packages | +|     **write:package** | Grants read/write access to packages | +|     **read:package** | Grants read access to packages | +|     **delete:package** | Grants delete access to packages | +| **admin:gpg_key** | Grants full access for managing GPG keys | +|     **write:gpg_key** | Grants read/write access to GPG keys | +|     **read:gpg_key** | Grants read-only access to GPG keys | +| **admin:application** | Grants full access to manage applications | +|     **write:application** | Grants read/write access for managing applications | +|     **read:application** | Grants read access for managing applications | +| **sudo** | Allows to perform actions as the site admin. | ## Client types diff --git a/docs/content/doc/help/faq.en-us.md b/docs/content/doc/help/faq.en-us.md index a92186334f38..2fcf0be15612 100644 --- a/docs/content/doc/help/faq.en-us.md +++ b/docs/content/doc/help/faq.en-us.md @@ -138,6 +138,8 @@ For more information, refer to Gitea's [API docs]({{< relref "doc/developers/api You can see the latest API (for example) on . +You can also see an example of the `swagger.json` file at . + ## Adjusting your server for public/private use ### Preventing spammers diff --git a/main.go b/main.go index 070e9857d08c..4b183a768609 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/setting" // register supported doc types + _ "code.gitea.io/gitea/modules/markup/asciicast" _ "code.gitea.io/gitea/modules/markup/console" _ "code.gitea.io/gitea/modules/markup/csv" _ "code.gitea.io/gitea/modules/markup/markdown" diff --git a/models/auth/token.go b/models/auth/token.go index 0dfcb7629ba9..3f9f117f7337 100644 --- a/models/auth/token.go +++ b/models/auth/token.go @@ -65,6 +65,7 @@ type AccessToken struct { TokenHash string `xorm:"UNIQUE"` // sha256 of token TokenSalt string TokenLastEight string `xorm:"INDEX token_last_eight"` + Scope AccessTokenScope CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` diff --git a/models/auth/token_scope.go b/models/auth/token_scope.go new file mode 100644 index 000000000000..c61c306496b4 --- /dev/null +++ b/models/auth/token_scope.go @@ -0,0 +1,251 @@ +// 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" + + 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 | + AccessTokenScopeNotificationBits | AccessTokenScopeUserBits | AccessTokenScopeDeleteRepoBits | + AccessTokenScopePackageBits | AccessTokenScopeAdminGPGKeyBits | AccessTokenScopeAdminApplicationBits + + AccessTokenScopeRepoBits AccessTokenScopeBitmap = 1< 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, + 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, + 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) { + list := strings.Split(string(s), ",") + + var bitmap AccessTokenScopeBitmap + for _, v := range list { + 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 +} + +// 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,notification,user,delete_repo,package,admin:gpg_key,admin:application", + "all", + )) + return scope +} diff --git a/models/auth/token_scope_test.go b/models/auth/token_scope_test.go new file mode 100644 index 000000000000..1d7f4794a477 --- /dev/null +++ b/models/auth/token_scope_test.go @@ -0,0 +1,84 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package auth + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAccessTokenScope_Normalize(t *testing.T) { + tests := []struct { + in AccessTokenScope + out AccessTokenScope + err error + }{ + {"", "", nil}, + {"repo", "repo", nil}, + {"repo,repo:status", "repo", nil}, + {"repo,public_repo", "repo", nil}, + {"admin:public_key,write:public_key", "admin:public_key", nil}, + {"admin:public_key,read:public_key", "admin:public_key", nil}, + {"write:public_key,read:public_key", "write:public_key", nil}, // read is include in write + {"admin:repo_hook,write:repo_hook", "admin:repo_hook", nil}, + {"admin:repo_hook,read:repo_hook", "admin:repo_hook", nil}, + {"repo,admin:repo_hook,read:repo_hook", "repo", nil}, // admin:repo_hook is a child scope of repo + {"repo,read:repo_hook", "repo", nil}, // read:repo_hook is a child scope of repo + {"user", "user", nil}, + {"user,read:user", "user", nil}, + {"user,admin:org,write:org", "admin:org,user", nil}, + {"admin:org,write:org,user", "admin:org,user", nil}, + {"package", "package", nil}, + {"package,write:package", "package", nil}, + {"package,write:package,delete:package", "package", nil}, + {"write:package,read:package", "write:package", nil}, // read is include in write + {"write:package,delete:package", "write:package,delete:package", nil}, // write and delete are not include in each other + {"admin:gpg_key", "admin:gpg_key", nil}, + {"admin:gpg_key,write:gpg_key", "admin:gpg_key", nil}, + {"admin:gpg_key,write:gpg_key,user", "user,admin:gpg_key", nil}, + {"admin:application,write:application,user", "user,admin:application", nil}, + {"all", "all", nil}, + {"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application", "all", nil}, + {"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application,sudo", "all,sudo", nil}, + } + + for _, test := range tests { + t.Run(string(test.in), func(t *testing.T) { + scope, err := test.in.Normalize() + assert.Equal(t, test.out, scope) + assert.Equal(t, test.err, err) + }) + } +} + +func TestAccessTokenScope_HasScope(t *testing.T) { + tests := []struct { + in AccessTokenScope + scope AccessTokenScope + out bool + err error + }{ + {"repo", "repo", true, nil}, + {"repo", "repo:status", true, nil}, + {"repo", "public_repo", true, nil}, + {"repo", "admin:org", false, nil}, + {"repo", "admin:public_key", false, nil}, + {"repo:status", "repo", false, nil}, + {"repo:status", "public_repo", false, nil}, + {"admin:org", "write:org", true, nil}, + {"admin:org", "read:org", true, nil}, + {"admin:org", "admin:org", true, nil}, + {"user", "read:user", true, nil}, + {"package", "write:package", true, nil}, + } + + for _, test := range tests { + t.Run(string(test.in), func(t *testing.T) { + scope, err := test.in.HasScope(test.scope) + assert.Equal(t, test.out, scope) + assert.Equal(t, test.err, err) + }) + } +} diff --git a/models/db/list_options.go b/models/db/list_options.go index b9ee360b1b85..2456f90f3b4f 100644 --- a/models/db/list_options.go +++ b/models/db/list_options.go @@ -4,8 +4,11 @@ package db import ( + "context" + "code.gitea.io/gitea/modules/setting" + "xorm.io/builder" "xorm.io/xorm" ) @@ -18,6 +21,7 @@ const ( type Paginator interface { GetSkipTake() (skip, take int) GetStartEnd() (start, end int) + IsListAll() bool } // GetPaginatedSession creates a paginated database session @@ -44,9 +48,12 @@ func SetEnginePagination(e Engine, p Paginator) Engine { // ListOptions options to paginate results type ListOptions struct { PageSize int - Page int // start from 1 + Page int // start from 1 + ListAll bool // if true, then PageSize and Page will not be taken } +var _ Paginator = &ListOptions{} + // GetSkipTake returns the skip and take values func (opts *ListOptions) GetSkipTake() (skip, take int) { opts.SetDefaultValues() @@ -60,6 +67,11 @@ func (opts *ListOptions) GetStartEnd() (start, end int) { return start, end } +// IsListAll indicates PageSize and Page will be ignored +func (opts *ListOptions) IsListAll() bool { + return opts.ListAll +} + // SetDefaultValues sets default values func (opts *ListOptions) SetDefaultValues() { if opts.PageSize <= 0 { @@ -79,6 +91,8 @@ type AbsoluteListOptions struct { take int } +var _ Paginator = &AbsoluteListOptions{} + // NewAbsoluteListOptions creates a list option with applied limits func NewAbsoluteListOptions(skip, take int) *AbsoluteListOptions { if skip < 0 { @@ -93,6 +107,11 @@ func NewAbsoluteListOptions(skip, take int) *AbsoluteListOptions { return &AbsoluteListOptions{skip, take} } +// IsListAll will always return false +func (opts *AbsoluteListOptions) IsListAll() bool { + return false +} + // GetSkipTake returns the skip and take values func (opts *AbsoluteListOptions) GetSkipTake() (skip, take int) { return opts.skip, opts.take @@ -102,3 +121,32 @@ func (opts *AbsoluteListOptions) GetSkipTake() (skip, take int) { func (opts *AbsoluteListOptions) GetStartEnd() (start, end int) { return opts.skip, opts.skip + opts.take } + +// FindOptions represents a find options +type FindOptions interface { + Paginator + ToConds() builder.Cond +} + +// Find represents a common find function which accept an options interface +func Find[T any](ctx context.Context, opts FindOptions, objects *[]T) error { + sess := GetEngine(ctx).Where(opts.ToConds()) + if !opts.IsListAll() { + sess.Limit(opts.GetSkipTake()) + } + return sess.Find(&objects) +} + +// Count represents a common count function which accept an options interface +func Count[T any](ctx context.Context, opts FindOptions, object T) (int64, error) { + return GetEngine(ctx).Where(opts.ToConds()).Count(object) +} + +// FindAndCount represents a common findandcount function which accept an options interface +func FindAndCount[T any](ctx context.Context, opts FindOptions, objects *[]T) (int64, error) { + sess := GetEngine(ctx).Where(opts.ToConds()) + if !opts.IsListAll() { + sess.Limit(opts.GetSkipTake()) + } + return sess.FindAndCount(&objects) +} diff --git a/models/issues/comment.go b/models/issues/comment.go index 2dcbc7d8198c..87e6b0a22909 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -8,9 +8,7 @@ package issues import ( "context" "fmt" - "regexp" "strconv" - "strings" "unicode/utf8" "code.gitea.io/gitea/models/db" @@ -22,8 +20,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" @@ -687,31 +683,6 @@ func (c *Comment) LoadReview() error { return c.loadReview(db.DefaultContext) } -var notEnoughLines = regexp.MustCompile(`fatal: file .* has only \d+ lines?`) - -func (c *Comment) checkInvalidation(doer *user_model.User, repo *git.Repository, branch string) error { - // FIXME differentiate between previous and proposed line - commit, err := repo.LineBlame(branch, repo.Path, c.TreePath, uint(c.UnsignedLine())) - if err != nil && (strings.Contains(err.Error(), "fatal: no such path") || notEnoughLines.MatchString(err.Error())) { - c.Invalidated = true - return UpdateComment(c, doer) - } - if err != nil { - return err - } - if c.CommitSHA != "" && c.CommitSHA != commit.ID.String() { - c.Invalidated = true - return UpdateComment(c, doer) - } - return nil -} - -// CheckInvalidation checks if the line of code comment got changed by another commit. -// If the line got changed the comment is going to be invalidated. -func (c *Comment) CheckInvalidation(repo *git.Repository, doer *user_model.User, branch string) error { - return c.checkInvalidation(doer, repo, branch) -} - // DiffSide returns "previous" if Comment.Line is a LOC of the previous changes and "proposed" if it is a LOC of the proposed changes. func (c *Comment) DiffSide() string { if c.Line < 0 { @@ -1008,23 +979,28 @@ func GetCommentByID(ctx context.Context, id int64) (*Comment, error) { // FindCommentsOptions describes the conditions to Find comments type FindCommentsOptions struct { db.ListOptions - RepoID int64 - IssueID int64 - ReviewID int64 - Since int64 - Before int64 - Line int64 - TreePath string - Type CommentType -} - -func (opts *FindCommentsOptions) toConds() builder.Cond { + RepoID int64 + IssueID int64 + ReviewID int64 + Since int64 + Before int64 + Line int64 + TreePath string + Type CommentType + IssueIDs []int64 + Invalidated util.OptionalBool +} + +// ToConds implements FindOptions interface +func (opts *FindCommentsOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"issue.repo_id": opts.RepoID}) } if opts.IssueID > 0 { cond = cond.And(builder.Eq{"comment.issue_id": opts.IssueID}) + } else if len(opts.IssueIDs) > 0 { + cond = cond.And(builder.In("comment.issue_id", opts.IssueIDs)) } if opts.ReviewID > 0 { cond = cond.And(builder.Eq{"comment.review_id": opts.ReviewID}) @@ -1044,13 +1020,16 @@ func (opts *FindCommentsOptions) toConds() builder.Cond { if len(opts.TreePath) > 0 { cond = cond.And(builder.Eq{"comment.tree_path": opts.TreePath}) } + if !opts.Invalidated.IsNone() { + cond = cond.And(builder.Eq{"comment.invalidated": opts.Invalidated.IsTrue()}) + } return cond } // FindComments returns all comments according options func FindComments(ctx context.Context, opts *FindCommentsOptions) ([]*Comment, error) { comments := make([]*Comment, 0, 10) - sess := db.GetEngine(ctx).Where(opts.toConds()) + sess := db.GetEngine(ctx).Where(opts.ToConds()) if opts.RepoID > 0 { sess.Join("INNER", "issue", "issue.id = comment.issue_id") } @@ -1069,13 +1048,19 @@ func FindComments(ctx context.Context, opts *FindCommentsOptions) ([]*Comment, e // CountComments count all comments according options by ignoring pagination func CountComments(opts *FindCommentsOptions) (int64, error) { - sess := db.GetEngine(db.DefaultContext).Where(opts.toConds()) + sess := db.GetEngine(db.DefaultContext).Where(opts.ToConds()) if opts.RepoID > 0 { sess.Join("INNER", "issue", "issue.id = comment.issue_id") } return sess.Count(&Comment{}) } +// UpdateCommentInvalidate updates comment invalidated column +func UpdateCommentInvalidate(ctx context.Context, c *Comment) error { + _, err := db.GetEngine(ctx).ID(c.ID).Cols("invalidated").Update(c) + return err +} + // UpdateComment updates information of comment. func UpdateComment(c *Comment, doer *user_model.User) error { ctx, committer, err := db.TxContext(db.DefaultContext) @@ -1134,120 +1119,6 @@ func DeleteComment(ctx context.Context, comment *Comment) error { return DeleteReaction(ctx, &ReactionOptions{CommentID: comment.ID}) } -// CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS -type CodeComments map[string]map[int64][]*Comment - -// FetchCodeComments will return a 2d-map: ["Path"]["Line"] = Comments at line -func FetchCodeComments(ctx context.Context, issue *Issue, currentUser *user_model.User) (CodeComments, error) { - return fetchCodeCommentsByReview(ctx, issue, currentUser, nil) -} - -func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *user_model.User, review *Review) (CodeComments, error) { - pathToLineToComment := make(CodeComments) - if review == nil { - review = &Review{ID: 0} - } - opts := FindCommentsOptions{ - Type: CommentTypeCode, - IssueID: issue.ID, - ReviewID: review.ID, - } - - comments, err := findCodeComments(ctx, opts, issue, currentUser, review) - if err != nil { - return nil, err - } - - for _, comment := range comments { - if pathToLineToComment[comment.TreePath] == nil { - pathToLineToComment[comment.TreePath] = make(map[int64][]*Comment) - } - pathToLineToComment[comment.TreePath][comment.Line] = append(pathToLineToComment[comment.TreePath][comment.Line], comment) - } - return pathToLineToComment, nil -} - -func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, currentUser *user_model.User, review *Review) ([]*Comment, error) { - var comments []*Comment - if review == nil { - review = &Review{ID: 0} - } - conds := opts.toConds() - if review.ID == 0 { - conds = conds.And(builder.Eq{"invalidated": false}) - } - e := db.GetEngine(ctx) - if err := e.Where(conds). - Asc("comment.created_unix"). - Asc("comment.id"). - Find(&comments); err != nil { - return nil, err - } - - if err := issue.LoadRepo(ctx); err != nil { - return nil, err - } - - if err := CommentList(comments).LoadPosters(ctx); err != nil { - return nil, err - } - - // Find all reviews by ReviewID - reviews := make(map[int64]*Review) - ids := make([]int64, 0, len(comments)) - for _, comment := range comments { - if comment.ReviewID != 0 { - ids = append(ids, comment.ReviewID) - } - } - if err := e.In("id", ids).Find(&reviews); err != nil { - return nil, err - } - - n := 0 - for _, comment := range comments { - if re, ok := reviews[comment.ReviewID]; ok && re != nil { - // If the review is pending only the author can see the comments (except if the review is set) - if review.ID == 0 && re.Type == ReviewTypePending && - (currentUser == nil || currentUser.ID != re.ReviewerID) { - continue - } - comment.Review = re - } - comments[n] = comment - n++ - - if err := comment.LoadResolveDoer(); err != nil { - return nil, err - } - - if err := comment.LoadReactions(issue.Repo); err != nil { - return nil, err - } - - var err error - if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: issue.Repo.Link(), - Metas: issue.Repo.ComposeMetas(), - }, comment.Content); err != nil { - return nil, err - } - } - return comments[:n], nil -} - -// FetchCodeCommentsByLine fetches the code comments for a given treePath and line number -func FetchCodeCommentsByLine(ctx context.Context, issue *Issue, currentUser *user_model.User, treePath string, line int64) ([]*Comment, error) { - opts := FindCommentsOptions{ - Type: CommentTypeCode, - IssueID: issue.ID, - TreePath: treePath, - Line: line, - } - return findCodeComments(ctx, opts, issue, currentUser, nil) -} - // UpdateCommentsMigrationsByType updates comments' migrations information via given git service type and original id and poster id func UpdateCommentsMigrationsByType(tp structs.GitServiceType, originalAuthorID string, posterID int64) error { _, err := db.GetEngine(db.DefaultContext).Table("comment"). diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go new file mode 100644 index 000000000000..8475da1a8c00 --- /dev/null +++ b/models/issues/comment_code.go @@ -0,0 +1,129 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package issues + +import ( + "context" + + "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/markup/markdown" + + "xorm.io/builder" +) + +// CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS +type CodeComments map[string]map[int64][]*Comment + +// FetchCodeComments will return a 2d-map: ["Path"]["Line"] = Comments at line +func FetchCodeComments(ctx context.Context, issue *Issue, currentUser *user_model.User) (CodeComments, error) { + return fetchCodeCommentsByReview(ctx, issue, currentUser, nil) +} + +func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *user_model.User, review *Review) (CodeComments, error) { + pathToLineToComment := make(CodeComments) + if review == nil { + review = &Review{ID: 0} + } + opts := FindCommentsOptions{ + Type: CommentTypeCode, + IssueID: issue.ID, + ReviewID: review.ID, + } + + comments, err := findCodeComments(ctx, opts, issue, currentUser, review) + if err != nil { + return nil, err + } + + for _, comment := range comments { + if pathToLineToComment[comment.TreePath] == nil { + pathToLineToComment[comment.TreePath] = make(map[int64][]*Comment) + } + pathToLineToComment[comment.TreePath][comment.Line] = append(pathToLineToComment[comment.TreePath][comment.Line], comment) + } + return pathToLineToComment, nil +} + +func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, currentUser *user_model.User, review *Review) ([]*Comment, error) { + var comments []*Comment + if review == nil { + review = &Review{ID: 0} + } + conds := opts.ToConds() + if review.ID == 0 { + conds = conds.And(builder.Eq{"invalidated": false}) + } + e := db.GetEngine(ctx) + if err := e.Where(conds). + Asc("comment.created_unix"). + Asc("comment.id"). + Find(&comments); err != nil { + return nil, err + } + + if err := issue.LoadRepo(ctx); err != nil { + return nil, err + } + + if err := CommentList(comments).LoadPosters(ctx); err != nil { + return nil, err + } + + // Find all reviews by ReviewID + reviews := make(map[int64]*Review) + ids := make([]int64, 0, len(comments)) + for _, comment := range comments { + if comment.ReviewID != 0 { + ids = append(ids, comment.ReviewID) + } + } + if err := e.In("id", ids).Find(&reviews); err != nil { + return nil, err + } + + n := 0 + for _, comment := range comments { + if re, ok := reviews[comment.ReviewID]; ok && re != nil { + // If the review is pending only the author can see the comments (except if the review is set) + if review.ID == 0 && re.Type == ReviewTypePending && + (currentUser == nil || currentUser.ID != re.ReviewerID) { + continue + } + comment.Review = re + } + comments[n] = comment + n++ + + if err := comment.LoadResolveDoer(); err != nil { + return nil, err + } + + if err := comment.LoadReactions(issue.Repo); err != nil { + return nil, err + } + + var err error + if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ + Ctx: ctx, + URLPrefix: issue.Repo.Link(), + Metas: issue.Repo.ComposeMetas(), + }, comment.Content); err != nil { + return nil, err + } + } + return comments[:n], nil +} + +// FetchCodeCommentsByLine fetches the code comments for a given treePath and line number +func FetchCodeCommentsByLine(ctx context.Context, issue *Issue, currentUser *user_model.User, treePath string, line int64) ([]*Comment, error) { + opts := FindCommentsOptions{ + Type: CommentTypeCode, + IssueID: issue.ID, + TreePath: treePath, + Line: line, + } + return findCodeComments(ctx, opts, issue, currentUser, nil) +} diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index 432e848e97ad..12dbff107d15 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -12,7 +12,6 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "xorm.io/xorm" @@ -161,7 +160,7 @@ func (prs PullRequestList) loadAttributes(ctx context.Context) error { } // Load issues. - issueIDs := prs.getIssueIDs() + issueIDs := prs.GetIssueIDs() issues := make([]*Issue, 0, len(issueIDs)) if err := db.GetEngine(ctx). Where("id > 0"). @@ -180,7 +179,8 @@ func (prs PullRequestList) loadAttributes(ctx context.Context) error { return nil } -func (prs PullRequestList) getIssueIDs() []int64 { +// GetIssueIDs returns all issue ids +func (prs PullRequestList) GetIssueIDs() []int64 { issueIDs := make([]int64, 0, len(prs)) for i := range prs { issueIDs = append(issueIDs, prs[i].IssueID) @@ -192,24 +192,3 @@ func (prs PullRequestList) getIssueIDs() []int64 { func (prs PullRequestList) LoadAttributes() error { return prs.loadAttributes(db.DefaultContext) } - -// InvalidateCodeComments will lookup the prs for code comments which got invalidated by change -func (prs PullRequestList) InvalidateCodeComments(ctx context.Context, doer *user_model.User, repo *git.Repository, branch string) error { - if len(prs) == 0 { - return nil - } - issueIDs := prs.getIssueIDs() - var codeComments []*Comment - if err := db.GetEngine(ctx). - Where("type = ? and invalidated = ?", CommentTypeCode, false). - In("issue_id", issueIDs). - Find(&codeComments); err != nil { - return fmt.Errorf("find code comments: %w", err) - } - for _, comment := range codeComments { - if err := comment.CheckInvalidation(repo, doer, branch); err != nil { - return err - } - } - return nil -} diff --git a/models/issues/review.go b/models/issues/review.go index d8e517ad3c32..7e1a39bb5b8b 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -972,7 +972,7 @@ func DeleteReview(r *Review) error { ReviewID: r.ID, } - if _, err := sess.Where(opts.toConds()).Delete(new(Comment)); err != nil { + if _, err := sess.Where(opts.ToConds()).Delete(new(Comment)); err != nil { return err } @@ -982,7 +982,7 @@ func DeleteReview(r *Review) error { ReviewID: r.ID, } - if _, err := sess.Where(opts.toConds()).Delete(new(Comment)); err != nil { + if _, err := sess.Where(opts.ToConds()).Delete(new(Comment)); err != nil { return err } @@ -1006,7 +1006,7 @@ func (r *Review) GetCodeCommentsCount() int { IssueID: r.IssueID, ReviewID: r.ID, } - conds := opts.toConds() + conds := opts.ToConds() if r.ID == 0 { conds = conds.And(builder.Eq{"invalidated": false}) } @@ -1026,7 +1026,7 @@ func (r *Review) HTMLURL() string { ReviewID: r.ID, } comment := new(Comment) - has, err := db.GetEngine(db.DefaultContext).Where(opts.toConds()).Get(comment) + has, err := db.GetEngine(db.DefaultContext).Where(opts.ToConds()).Get(comment) if err != nil || !has { return "" } diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 4e211617c0ff..2058fcec0fe0 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -451,6 +451,8 @@ var migrations = []Migration{ NewMigration("Drop ForeignReference table", v1_19.DropForeignReferenceTable), // v238 -> v239 NewMigration("Add updated unix to LFSMetaObject", v1_19.AddUpdatedUnixToLFSMetaObject), + // v239 -> v240 + NewMigration("Add scope for access_token", v1_19.AddScopeForAccessTokens), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_19/v239.go b/models/migrations/v1_19/v239.go new file mode 100644 index 000000000000..10076f240169 --- /dev/null +++ b/models/migrations/v1_19/v239.go @@ -0,0 +1,22 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_19 //nolint + +import ( + "xorm.io/xorm" +) + +func AddScopeForAccessTokens(x *xorm.Engine) error { + type AccessToken struct { + Scope string + } + + if err := x.Sync(new(AccessToken)); err != nil { + return err + } + + // all previous tokens have `all` and `sudo` scopes + _, err := x.Exec("UPDATE access_token SET scope = ? WHERE scope IS NULL OR scope = ''", "all,sudo") + return err +} diff --git a/models/system/setting.go b/models/system/setting.go index 0701c4bf48dc..50fe17498ef3 100644 --- a/models/system/setting.go +++ b/models/system/setting.go @@ -268,6 +268,16 @@ func Init() error { if setting_module.OfflineMode { disableGravatar = true enableFederatedAvatar = false + if !GetSettingBool(KeyPictureDisableGravatar) { + if err := SetSettingNoVersion(KeyPictureDisableGravatar, "true"); err != nil { + return fmt.Errorf("Failed to set setting %q: %w", KeyPictureDisableGravatar, err) + } + } + if GetSettingBool(KeyPictureEnableFederatedAvatar) { + if err := SetSettingNoVersion(KeyPictureEnableFederatedAvatar, "false"); err != nil { + return fmt.Errorf("Failed to set setting %q: %w", KeyPictureEnableFederatedAvatar, err) + } + } } if enableFederatedAvatar || !disableGravatar { diff --git a/modules/markup/asciicast/asciicast.go b/modules/markup/asciicast/asciicast.go new file mode 100644 index 000000000000..06780623403a --- /dev/null +++ b/modules/markup/asciicast/asciicast.go @@ -0,0 +1,64 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package asciicast + +import ( + "fmt" + "io" + "net/url" + "regexp" + + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/setting" +) + +func init() { + markup.RegisterRenderer(Renderer{}) +} + +// Renderer implements markup.Renderer for asciicast files. +// See https://github.com/asciinema/asciinema/blob/develop/doc/asciicast-v2.md +type Renderer struct{} + +// Name implements markup.Renderer +func (Renderer) Name() string { + return "asciicast" +} + +// Extensions implements markup.Renderer +func (Renderer) Extensions() []string { + return []string{".cast"} +} + +const ( + playerClassName = "asciinema-player-container" + playerSrcAttr = "data-asciinema-player-src" +) + +// SanitizerRules implements markup.Renderer +func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule { + return []setting.MarkupSanitizerRule{ + {Element: "div", AllowAttr: "class", Regexp: regexp.MustCompile(playerClassName)}, + {Element: "div", AllowAttr: playerSrcAttr}, + } +} + +// Render implements markup.Renderer +func (Renderer) Render(ctx *markup.RenderContext, _ io.Reader, output io.Writer) error { + rawURL := fmt.Sprintf("%s/%s/%s/raw/%s/%s", + setting.AppSubURL, + url.PathEscape(ctx.Metas["user"]), + url.PathEscape(ctx.Metas["repo"]), + ctx.Metas["BranchNameSubURL"], + url.PathEscape(ctx.RelativePath), + ) + + _, err := io.WriteString(output, fmt.Sprintf( + `
`, + playerClassName, + playerSrcAttr, + rawURL, + )) + return err +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 1cd4e6a1d03d..9d1d279b0e85 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -747,6 +747,7 @@ access_token_deletion_cancel_action = Cancel access_token_deletion_confirm_action = Delete access_token_deletion_desc = Deleting a token will revoke access to your account for applications using it. This cannot be undone. Continue? delete_token_success = The token has been deleted. Applications using it no longer have access to your account. +select_scopes = Select scopes manage_oauth2_applications = Manage OAuth2 Applications edit_oauth2_application = Edit OAuth2 Application diff --git a/package-lock.json b/package-lock.json index a301dcddb301..af317e1721cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@primer/octicons": "17.10.0", "@vue/compiler-sfc": "3.2.45", "add-asset-webpack-plugin": "2.0.1", + "asciinema-player": "3.0.1", "css-loader": "6.7.3", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", @@ -197,6 +198,17 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@braintree/sanitize-url": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz", @@ -2094,6 +2106,15 @@ "printable-characters": "^1.0.42" } }, + "node_modules/asciinema-player": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/asciinema-player/-/asciinema-player-3.0.1.tgz", + "integrity": "sha512-plm/C/MhOtZWysrfcT/rzxOuu8vxvvDSvF50pqZS6KpJUDmATedAhO54zktbE/g7RiaaYfzgX8xjRhlQdgISwA==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "solid-js": "^1.3.0" + } + }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -7754,6 +7775,11 @@ "node": ">=8" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, "node_modules/regexp-tree": { "version": "0.1.24", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", @@ -8249,6 +8275,19 @@ "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", "dev": true }, + "node_modules/solid-js": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.6.9.tgz", + "integrity": "sha512-kV3fMmm+1C2J95c8eDOPKGfZHnuAkHUBLG4hX1Xu08bXeAIPqmxuz/QdH3B8SIdTp3EatBVIyA6RCes3hrGzpg==", + "dependencies": { + "csstype": "^3.1.0" + } + }, + "node_modules/solid-js/node_modules/csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, "node_modules/sortablejs": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz", @@ -10159,6 +10198,14 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==" }, + "@babel/runtime": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, "@braintree/sanitize-url": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz", @@ -11525,6 +11572,15 @@ "printable-characters": "^1.0.42" } }, + "asciinema-player": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/asciinema-player/-/asciinema-player-3.0.1.tgz", + "integrity": "sha512-plm/C/MhOtZWysrfcT/rzxOuu8vxvvDSvF50pqZS6KpJUDmATedAhO54zktbE/g7RiaaYfzgX8xjRhlQdgISwA==", + "requires": { + "@babel/runtime": "^7.15.4", + "solid-js": "^1.3.0" + } + }, "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -15606,6 +15662,11 @@ "strip-indent": "^3.0.0" } }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, "regexp-tree": { "version": "0.1.24", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", @@ -15960,6 +16021,21 @@ "socks": "^2.3.3" } }, + "solid-js": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.6.9.tgz", + "integrity": "sha512-kV3fMmm+1C2J95c8eDOPKGfZHnuAkHUBLG4hX1Xu08bXeAIPqmxuz/QdH3B8SIdTp3EatBVIyA6RCes3hrGzpg==", + "requires": { + "csstype": "^3.1.0" + }, + "dependencies": { + "csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + } + } + }, "sortablejs": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz", diff --git a/package.json b/package.json index 229d4f1aa90a..1ad52f9cd39a 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@primer/octicons": "17.10.0", "@vue/compiler-sfc": "3.2.45", "add-asset-webpack-plugin": "2.0.1", + "asciinema-player": "3.0.1", "css-loader": "6.7.3", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index d5a12ead85fd..cd08aae4145c 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -69,6 +69,7 @@ import ( "net/http" "strings" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" @@ -206,9 +207,36 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext) } // Contexter middleware already checks token for user sign in process. -func reqToken() func(ctx *context.APIContext) { +func reqToken(requiredScope auth_model.AccessTokenScope) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { - if true == ctx.Data["IsApiToken"] { + // If OAuth2 token is present + if _, ok := ctx.Data["ApiTokenScope"]; ctx.Data["IsApiToken"] == true && ok { + // no scope required + if requiredScope == "" { + return + } + + // check scope + scope := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope) + allow, err := scope.HasScope(requiredScope) + if err != nil { + ctx.Error(http.StatusForbidden, "reqToken", "parsing token failed: "+err.Error()) + return + } + if allow { + return + } + + // if requires 'repo' scope, but only has 'public_repo' scope, allow it only if the repo is public + if requiredScope == auth_model.AccessTokenScopeRepo { + if allowPublicRepo, err := scope.HasScope(auth_model.AccessTokenScopePublicRepo); err == nil && allowPublicRepo { + if ctx.Repo.Repository != nil && !ctx.Repo.Repository.IsPrivate { + return + } + } + } + + ctx.Error(http.StatusForbidden, "reqToken", "token does not have required scope: "+requiredScope) return } if ctx.Context.IsBasicAuth { @@ -631,7 +659,7 @@ func Routes(ctx gocontext.Context) *web.Route { })) m.Group("", func() { - // Miscellaneous + // Miscellaneous (no scope required) if setting.API.EnableSwagger { m.Get("/swagger", func(ctx *context.APIContext) { ctx.Redirect(setting.AppSubURL + "/api/swagger") @@ -657,7 +685,7 @@ func Routes(ctx gocontext.Context) *web.Route { m.Get("/repository", settings.GetGeneralRepoSettings) }) - // Notifications + // Notifications (requires 'notification' scope) m.Group("/notifications", func() { m.Combo(""). Get(notify.ListNotifications). @@ -666,9 +694,9 @@ func Routes(ctx gocontext.Context) *web.Route { m.Combo("/threads/{id}"). Get(notify.GetThread). Patch(notify.ReadThread) - }, reqToken()) + }, reqToken(auth_model.AccessTokenScopeNotification)) - // Users + // Users (no scope required) m.Group("/users", func() { m.Get("/search", reqExploreSignIn(), user.Search) @@ -688,6 +716,7 @@ func Routes(ctx gocontext.Context) *web.Route { }, context_service.UserAssignmentAPI()) }) + // (no scope required) m.Group("/users", func() { m.Group("/{username}", func() { m.Get("/keys", user.ListPublicKeys) @@ -703,57 +732,62 @@ func Routes(ctx gocontext.Context) *web.Route { m.Get("/subscriptions", user.GetWatchedRepos) }, context_service.UserAssignmentAPI()) - }, reqToken()) + }, reqToken("")) m.Group("/user", func() { m.Get("", user.GetAuthenticatedUser) m.Group("/settings", func() { - m.Get("", user.GetUserSettings) - m.Patch("", bind(api.UserSettingsOptions{}), user.UpdateUserSettings) - }, reqToken()) - m.Combo("/emails").Get(user.ListEmails). - Post(bind(api.CreateEmailOption{}), user.AddEmail). - Delete(bind(api.DeleteEmailOption{}), user.DeleteEmail) + m.Get("", reqToken(auth_model.AccessTokenScopeReadUser), user.GetUserSettings) + m.Patch("", reqToken(auth_model.AccessTokenScopeUser), bind(api.UserSettingsOptions{}), user.UpdateUserSettings) + }) + m.Combo("/emails").Get(reqToken(auth_model.AccessTokenScopeReadUser), user.ListEmails). + Post(reqToken(auth_model.AccessTokenScopeUser), bind(api.CreateEmailOption{}), user.AddEmail). + Delete(reqToken(auth_model.AccessTokenScopeUser), bind(api.DeleteEmailOption{}), user.DeleteEmail) m.Get("/followers", user.ListMyFollowers) m.Group("/following", func() { m.Get("", user.ListMyFollowing) m.Group("/{username}", func() { m.Get("", user.CheckMyFollowing) - m.Put("", user.Follow) - m.Delete("", user.Unfollow) + m.Put("", reqToken(auth_model.AccessTokenScopeUserFollow), user.Follow) // requires 'user:follow' scope + m.Delete("", reqToken(auth_model.AccessTokenScopeUserFollow), user.Unfollow) // requires 'user:follow' scope }, context_service.UserAssignmentAPI()) }) + // (admin:public_key scope) m.Group("/keys", func() { - m.Combo("").Get(user.ListMyPublicKeys). - Post(bind(api.CreateKeyOption{}), user.CreatePublicKey) - m.Combo("/{id}").Get(user.GetPublicKey). - Delete(user.DeletePublicKey) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadPublicKey), user.ListMyPublicKeys). + Post(reqToken(auth_model.AccessTokenScopeWritePublicKey), bind(api.CreateKeyOption{}), user.CreatePublicKey) + m.Combo("/{id}").Get(reqToken(auth_model.AccessTokenScopeReadPublicKey), user.GetPublicKey). + Delete(reqToken(auth_model.AccessTokenScopeWritePublicKey), user.DeletePublicKey) }) + + // (admin:application scope) m.Group("/applications", func() { m.Combo("/oauth2"). - Get(user.ListOauth2Applications). - Post(bind(api.CreateOAuth2ApplicationOptions{}), user.CreateOauth2Application) + Get(reqToken(auth_model.AccessTokenScopeReadApplication), user.ListOauth2Applications). + Post(reqToken(auth_model.AccessTokenScopeWriteApplication), bind(api.CreateOAuth2ApplicationOptions{}), user.CreateOauth2Application) m.Combo("/oauth2/{id}"). - Delete(user.DeleteOauth2Application). - Patch(bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application). - Get(user.GetOauth2Application) - }, reqToken()) + Delete(reqToken(auth_model.AccessTokenScopeWriteApplication), user.DeleteOauth2Application). + Patch(reqToken(auth_model.AccessTokenScopeWriteApplication), bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application). + Get(reqToken(auth_model.AccessTokenScopeReadApplication), user.GetOauth2Application) + }) + // (admin:gpg_key scope) m.Group("/gpg_keys", func() { - m.Combo("").Get(user.ListMyGPGKeys). - Post(bind(api.CreateGPGKeyOption{}), user.CreateGPGKey) - m.Combo("/{id}").Get(user.GetGPGKey). - Delete(user.DeleteGPGKey) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadGPGKey), user.ListMyGPGKeys). + Post(reqToken(auth_model.AccessTokenScopeWriteGPGKey), bind(api.CreateGPGKeyOption{}), user.CreateGPGKey) + m.Combo("/{id}").Get(reqToken(auth_model.AccessTokenScopeReadGPGKey), user.GetGPGKey). + Delete(reqToken(auth_model.AccessTokenScopeWriteGPGKey), user.DeleteGPGKey) }) + m.Get("/gpg_key_token", reqToken(auth_model.AccessTokenScopeReadGPGKey), user.GetVerificationToken) + m.Post("/gpg_key_verify", reqToken(auth_model.AccessTokenScopeReadGPGKey), bind(api.VerifyGPGKeyOption{}), user.VerifyUserGPGKey) - m.Get("/gpg_key_token", user.GetVerificationToken) - m.Post("/gpg_key_verify", bind(api.VerifyGPGKeyOption{}), user.VerifyUserGPGKey) - - m.Combo("/repos").Get(user.ListMyRepos). + // (repo scope) + m.Combo("/repos", reqToken(auth_model.AccessTokenScopeRepo)).Get(user.ListMyRepos). Post(bind(api.CreateRepoOption{}), repo.Create) + // (repo scope) m.Group("/starred", func() { m.Get("", user.GetMyStarredRepos) m.Group("/{username}/{reponame}", func() { @@ -761,57 +795,57 @@ func Routes(ctx gocontext.Context) *web.Route { m.Put("", user.Star) m.Delete("", user.Unstar) }, repoAssignment()) - }) - m.Get("/times", repo.ListMyTrackedTimes) - - m.Get("/stopwatches", repo.GetStopwatches) - - m.Get("/subscriptions", user.GetMyWatchedRepos) - - m.Get("/teams", org.ListUserTeams) - }, reqToken()) + }, reqToken(auth_model.AccessTokenScopeRepo)) + m.Get("/times", reqToken(auth_model.AccessTokenScopeRepo), repo.ListMyTrackedTimes) + m.Get("/stopwatches", reqToken(auth_model.AccessTokenScopeRepo), repo.GetStopwatches) + m.Get("/subscriptions", reqToken(auth_model.AccessTokenScopeRepo), user.GetMyWatchedRepos) + m.Get("/teams", reqToken(auth_model.AccessTokenScopeRepo), org.ListUserTeams) + }, reqToken("")) // Repositories - m.Post("/org/{org}/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated) + m.Post("/org/{org}/repos", reqToken(auth_model.AccessTokenScopeAdminOrg), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated) - m.Combo("/repositories/{id}", reqToken()).Get(repo.GetByID) + m.Combo("/repositories/{id}", reqToken(auth_model.AccessTokenScopeRepo)).Get(repo.GetByID) m.Group("/repos", func() { m.Get("/search", repo.Search) m.Get("/issues/search", repo.SearchIssues) - m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate) + // (repo scope) + m.Post("/migrate", reqToken(auth_model.AccessTokenScopeRepo), bind(api.MigrateRepoOptions{}), repo.Migrate) m.Group("/{username}/{reponame}", func() { m.Combo("").Get(reqAnyRepoReader(), repo.Get). - Delete(reqToken(), reqOwner(), repo.Delete). - Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) - m.Post("/generate", reqToken(), reqRepoReader(unit.TypeCode), bind(api.GenerateRepoOption{}), repo.Generate) - m.Post("/transfer", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer) - m.Post("/transfer/accept", reqToken(), repo.AcceptTransfer) - m.Post("/transfer/reject", reqToken(), repo.RejectTransfer) - m.Combo("/notifications"). - Get(reqToken(), notify.ListRepoNotifications). - Put(reqToken(), notify.ReadRepoNotifications) + Delete(reqToken(auth_model.AccessTokenScopeDeleteRepo), reqOwner(), repo.Delete). + Patch(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) + m.Post("/generate", reqToken(auth_model.AccessTokenScopeRepo), reqRepoReader(unit.TypeCode), bind(api.GenerateRepoOption{}), repo.Generate) + m.Group("/transfer", func() { + m.Post("", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer) + m.Post("/accept", repo.AcceptTransfer) + m.Post("/reject", repo.RejectTransfer) + }, reqToken(auth_model.AccessTokenScopeRepo)) + m.Combo("/notifications", reqToken(auth_model.AccessTokenScopeNotification)). + Get(notify.ListRepoNotifications). + Put(notify.ReadRepoNotifications) m.Group("/hooks/git", func() { - m.Combo("").Get(repo.ListGitHooks) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.ListGitHooks) m.Group("/{id}", func() { - m.Combo("").Get(repo.GetGitHook). - Patch(bind(api.EditGitHookOption{}), repo.EditGitHook). - Delete(repo.DeleteGitHook) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.GetGitHook). + Patch(reqToken(auth_model.AccessTokenScopeWriteRepoHook), bind(api.EditGitHookOption{}), repo.EditGitHook). + Delete(reqToken(auth_model.AccessTokenScopeWriteRepoHook), repo.DeleteGitHook) }) - }, reqToken(), reqAdmin(), reqGitHook(), context.ReferencesGitRepo(true)) + }, reqAdmin(), reqGitHook(), context.ReferencesGitRepo(true)) m.Group("/hooks", func() { - m.Combo("").Get(repo.ListHooks). - Post(bind(api.CreateHookOption{}), repo.CreateHook) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.ListHooks). + Post(reqToken(auth_model.AccessTokenScopeWriteRepoHook), bind(api.CreateHookOption{}), repo.CreateHook) m.Group("/{id}", func() { - m.Combo("").Get(repo.GetHook). - Patch(bind(api.EditHookOption{}), repo.EditHook). - Delete(repo.DeleteHook) - m.Post("/tests", context.ReferencesGitRepo(), context.RepoRefForAPI, repo.TestHook) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.GetHook). + Patch(reqToken(auth_model.AccessTokenScopeWriteRepoHook), bind(api.EditHookOption{}), repo.EditHook). + Delete(reqToken(auth_model.AccessTokenScopeWriteRepoHook), repo.DeleteHook) + m.Post("/tests", reqToken(auth_model.AccessTokenScopeReadRepoHook), context.ReferencesGitRepo(), context.RepoRefForAPI, repo.TestHook) }) - }, reqToken(), reqAdmin(), reqWebhooksEnabled()) + }, reqAdmin(), reqWebhooksEnabled()) m.Group("/collaborators", func() { m.Get("", reqAnyRepoReader(), repo.ListCollaborators) m.Group("/{collaborator}", func() { @@ -819,26 +853,26 @@ func Routes(ctx gocontext.Context) *web.Route { Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator). Delete(reqAdmin(), repo.DeleteCollaborator) m.Get("/permission", repo.GetRepoPermissions) - }, reqToken()) - }, reqToken()) - m.Get("/assignees", reqToken(), reqAnyRepoReader(), repo.GetAssignees) - m.Get("/reviewers", reqToken(), reqAnyRepoReader(), repo.GetReviewers) + }) + }, reqToken(auth_model.AccessTokenScopeRepo)) + m.Get("/assignees", reqToken(auth_model.AccessTokenScopeRepo), reqAnyRepoReader(), repo.GetAssignees) + m.Get("/reviewers", reqToken(auth_model.AccessTokenScopeRepo), reqAnyRepoReader(), repo.GetReviewers) m.Group("/teams", func() { m.Get("", reqAnyRepoReader(), repo.ListTeams) m.Combo("/{team}").Get(reqAnyRepoReader(), repo.IsTeam). Put(reqAdmin(), repo.AddTeam). Delete(reqAdmin(), repo.DeleteTeam) - }, reqToken()) + }, reqToken(auth_model.AccessTokenScopeRepo)) m.Get("/raw/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFile) m.Get("/media/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFileOrLFS) m.Get("/archive/*", reqRepoReader(unit.TypeCode), repo.GetArchive) m.Combo("/forks").Get(repo.ListForks). - Post(reqToken(), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork) + Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork) m.Group("/branches", func() { m.Get("", repo.ListBranches) m.Get("/*", repo.GetBranch) - m.Delete("/*", reqRepoWriter(unit.TypeCode), repo.DeleteBranch) - m.Post("", reqRepoWriter(unit.TypeCode), bind(api.CreateBranchRepoOption{}), repo.CreateBranch) + m.Delete("/*", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), repo.DeleteBranch) + m.Post("", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), bind(api.CreateBranchRepoOption{}), repo.CreateBranch) }, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode)) m.Group("/branch_protections", func() { m.Get("", repo.ListBranchProtections) @@ -848,74 +882,74 @@ func Routes(ctx gocontext.Context) *web.Route { m.Patch("", bind(api.EditBranchProtectionOption{}), repo.EditBranchProtection) m.Delete("", repo.DeleteBranchProtection) }) - }, reqToken(), reqAdmin()) + }, reqToken(auth_model.AccessTokenScopeRepo), reqAdmin()) m.Group("/tags", func() { m.Get("", repo.ListTags) m.Get("/*", repo.GetTag) - m.Post("", reqRepoWriter(unit.TypeCode), bind(api.CreateTagOption{}), repo.CreateTag) - m.Delete("/*", repo.DeleteTag) + m.Post("", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), bind(api.CreateTagOption{}), repo.CreateTag) + m.Delete("/*", reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteTag) }, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(true)) m.Group("/keys", func() { m.Combo("").Get(repo.ListDeployKeys). Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) m.Combo("/{id}").Get(repo.GetDeployKey). Delete(repo.DeleteDeploykey) - }, reqToken(), reqAdmin()) + }, reqToken(auth_model.AccessTokenScopeRepo), reqAdmin()) m.Group("/times", func() { m.Combo("").Get(repo.ListTrackedTimesByRepository) m.Combo("/{timetrackingusername}").Get(repo.ListTrackedTimesByUser) - }, mustEnableIssues, reqToken()) + }, mustEnableIssues, reqToken(auth_model.AccessTokenScopeRepo)) m.Group("/wiki", func() { m.Combo("/page/{pageName}"). Get(repo.GetWikiPage). - Patch(mustNotBeArchived, reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.EditWikiPage). - Delete(mustNotBeArchived, reqRepoWriter(unit.TypeWiki), repo.DeleteWikiPage) + Patch(mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.EditWikiPage). + Delete(mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeWiki), repo.DeleteWikiPage) m.Get("/revisions/{pageName}", repo.ListPageRevisions) - m.Post("/new", mustNotBeArchived, reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.NewWikiPage) + m.Post("/new", mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.NewWikiPage) m.Get("/pages", repo.ListWikiPages) }, mustEnableWiki) m.Group("/issues", func() { m.Combo("").Get(repo.ListIssues). - Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue) + Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue) m.Group("/comments", func() { m.Get("", repo.ListRepoIssueComments) m.Group("/{id}", func() { m.Combo(""). Get(repo.GetIssueComment). - Patch(mustNotBeArchived, reqToken(), bind(api.EditIssueCommentOption{}), repo.EditIssueComment). - Delete(reqToken(), repo.DeleteIssueComment) + Patch(mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditIssueCommentOption{}), repo.EditIssueComment). + Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteIssueComment) m.Combo("/reactions"). Get(repo.GetIssueCommentReactions). - Post(reqToken(), bind(api.EditReactionOption{}), repo.PostIssueCommentReaction). - Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction) + Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.PostIssueCommentReaction). + Delete(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction) m.Group("/assets", func() { m.Combo(""). Get(repo.ListIssueCommentAttachments). - Post(reqToken(), mustNotBeArchived, repo.CreateIssueCommentAttachment) + Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.CreateIssueCommentAttachment) m.Combo("/{asset}"). Get(repo.GetIssueCommentAttachment). - Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment). - Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueCommentAttachment) + Patch(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment). + Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.DeleteIssueCommentAttachment) }, mustEnableAttachments) }) }) m.Group("/{index}", func() { m.Combo("").Get(repo.GetIssue). - Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue). - Delete(reqToken(), reqAdmin(), context.ReferencesGitRepo(), repo.DeleteIssue) + Patch(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditIssueOption{}), repo.EditIssue). + Delete(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), context.ReferencesGitRepo(), repo.DeleteIssue) m.Group("/comments", func() { m.Combo("").Get(repo.ListIssueComments). - Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) - m.Combo("/{id}", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). + Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) + m.Combo("/{id}", reqToken(auth_model.AccessTokenScopeRepo)).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). Delete(repo.DeleteIssueCommentDeprecated) }) m.Get("/timeline", repo.ListIssueCommentsAndTimeline) m.Group("/labels", func() { m.Combo("").Get(repo.ListIssueLabels). - Post(reqToken(), bind(api.IssueLabelsOption{}), repo.AddIssueLabels). - Put(reqToken(), bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels). - Delete(reqToken(), repo.ClearIssueLabels) - m.Delete("/{id}", reqToken(), repo.DeleteIssueLabel) + Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueLabelsOption{}), repo.AddIssueLabels). + Put(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels). + Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.ClearIssueLabels) + m.Delete("/{id}", reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteIssueLabel) }) m.Group("/times", func() { m.Combo(""). @@ -923,125 +957,125 @@ func Routes(ctx gocontext.Context) *web.Route { Post(bind(api.AddTimeOption{}), repo.AddTime). Delete(repo.ResetIssueTime) m.Delete("/{id}", repo.DeleteTime) - }, reqToken()) - m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) + }, reqToken(auth_model.AccessTokenScopeRepo)) + m.Combo("/deadline").Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) m.Group("/stopwatch", func() { - m.Post("/start", reqToken(), repo.StartIssueStopwatch) - m.Post("/stop", reqToken(), repo.StopIssueStopwatch) - m.Delete("/delete", reqToken(), repo.DeleteIssueStopwatch) + m.Post("/start", reqToken(auth_model.AccessTokenScopeRepo), repo.StartIssueStopwatch) + m.Post("/stop", reqToken(auth_model.AccessTokenScopeRepo), repo.StopIssueStopwatch) + m.Delete("/delete", reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteIssueStopwatch) }) m.Group("/subscriptions", func() { m.Get("", repo.GetIssueSubscribers) - m.Get("/check", reqToken(), repo.CheckIssueSubscription) - m.Put("/{user}", reqToken(), repo.AddIssueSubscription) - m.Delete("/{user}", reqToken(), repo.DelIssueSubscription) + m.Get("/check", reqToken(auth_model.AccessTokenScopeRepo), repo.CheckIssueSubscription) + m.Put("/{user}", reqToken(auth_model.AccessTokenScopeRepo), repo.AddIssueSubscription) + m.Delete("/{user}", reqToken(auth_model.AccessTokenScopeRepo), repo.DelIssueSubscription) }) m.Combo("/reactions"). Get(repo.GetIssueReactions). - Post(reqToken(), bind(api.EditReactionOption{}), repo.PostIssueReaction). - Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueReaction) + Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.PostIssueReaction). + Delete(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.DeleteIssueReaction) m.Group("/assets", func() { m.Combo(""). Get(repo.ListIssueAttachments). - Post(reqToken(), mustNotBeArchived, repo.CreateIssueAttachment) + Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.CreateIssueAttachment) m.Combo("/{asset}"). Get(repo.GetIssueAttachment). - Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment). - Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueAttachment) + Patch(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment). + Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.DeleteIssueAttachment) }, mustEnableAttachments) }) }, mustEnableIssuesOrPulls) m.Group("/labels", func() { m.Combo("").Get(repo.ListLabels). - Post(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel) + Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel) m.Combo("/{id}").Get(repo.GetLabel). - Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel). - Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteLabel) + Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel). + Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteLabel) }) - m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown) - m.Post("/markdown/raw", misc.MarkdownRaw) + m.Post("/markdown", reqToken(auth_model.AccessTokenScopeRepo), bind(api.MarkdownOption{}), misc.Markdown) + m.Post("/markdown/raw", reqToken(auth_model.AccessTokenScopeRepo), misc.MarkdownRaw) m.Group("/milestones", func() { m.Combo("").Get(repo.ListMilestones). - Post(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone) + Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone) m.Combo("/{id}").Get(repo.GetMilestone). - Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). - Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) + Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). + Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) }) m.Get("/stargazers", repo.ListStargazers) m.Get("/subscribers", repo.ListSubscribers) m.Group("/subscription", func() { m.Get("", user.IsWatching) - m.Put("", reqToken(), user.Watch) - m.Delete("", reqToken(), user.Unwatch) + m.Put("", reqToken(auth_model.AccessTokenScopeRepo), user.Watch) + m.Delete("", reqToken(auth_model.AccessTokenScopeRepo), user.Unwatch) }) m.Group("/releases", func() { m.Combo("").Get(repo.ListReleases). - Post(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease) + Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease) m.Group("/{id}", func() { m.Combo("").Get(repo.GetRelease). - Patch(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). - Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteRelease) + Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). + Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.DeleteRelease) m.Group("/assets", func() { m.Combo("").Get(repo.ListReleaseAttachments). - Post(reqToken(), reqRepoWriter(unit.TypeReleases), repo.CreateReleaseAttachment) + Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.CreateReleaseAttachment) m.Combo("/{asset}").Get(repo.GetReleaseAttachment). - Patch(reqToken(), reqRepoWriter(unit.TypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). - Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseAttachment) + Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). + Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseAttachment) }) }) m.Group("/tags", func() { m.Combo("/{tag}"). Get(repo.GetReleaseByTag). - Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseByTag) + Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseByTag) }) }, reqRepoReader(unit.TypeReleases)) - m.Post("/mirror-sync", reqToken(), reqRepoWriter(unit.TypeCode), repo.MirrorSync) - m.Post("/push_mirrors-sync", reqAdmin(), repo.PushMirrorSync) + m.Post("/mirror-sync", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), repo.MirrorSync) + m.Post("/push_mirrors-sync", reqAdmin(), reqToken(auth_model.AccessTokenScopeRepo), repo.PushMirrorSync) m.Group("/push_mirrors", func() { m.Combo("").Get(repo.ListPushMirrors). Post(bind(api.CreatePushMirrorOption{}), repo.AddPushMirror) m.Combo("/{name}"). Delete(repo.DeletePushMirrorByRemoteName). Get(repo.GetPushMirrorByName) - }, reqAdmin()) + }, reqAdmin(), reqToken(auth_model.AccessTokenScopeRepo)) m.Get("/editorconfig/{filename}", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig) m.Group("/pulls", func() { m.Combo("").Get(repo.ListPullRequests). - Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) + Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) m.Group("/{index}", func() { m.Combo("").Get(repo.GetPullRequest). - Patch(reqToken(), bind(api.EditPullRequestOption{}), repo.EditPullRequest) + Patch(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditPullRequestOption{}), repo.EditPullRequest) m.Get(".{diffType:diff|patch}", repo.DownloadPullDiffOrPatch) - m.Post("/update", reqToken(), repo.UpdatePullRequest) + m.Post("/update", reqToken(auth_model.AccessTokenScopeRepo), repo.UpdatePullRequest) m.Get("/commits", repo.GetPullRequestCommits) m.Get("/files", repo.GetPullRequestFiles) m.Combo("/merge").Get(repo.IsPullRequestMerged). - Post(reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest). - Delete(reqToken(), mustNotBeArchived, repo.CancelScheduledAutoMerge) + Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest). + Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.CancelScheduledAutoMerge) m.Group("/reviews", func() { m.Combo(""). Get(repo.ListPullReviews). - Post(reqToken(), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview) + Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview) m.Group("/{id}", func() { m.Combo(""). Get(repo.GetPullReview). - Delete(reqToken(), repo.DeletePullReview). - Post(reqToken(), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview) + Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.DeletePullReview). + Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview) m.Combo("/comments"). Get(repo.GetPullReviewComments) - m.Post("/dismissals", reqToken(), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview) - m.Post("/undismissals", reqToken(), repo.UnDismissPullReview) + m.Post("/dismissals", reqToken(auth_model.AccessTokenScopeRepo), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview) + m.Post("/undismissals", reqToken(auth_model.AccessTokenScopeRepo), repo.UnDismissPullReview) }) }) - m.Combo("/requested_reviewers"). - Delete(reqToken(), bind(api.PullReviewRequestOptions{}), repo.DeleteReviewRequests). - Post(reqToken(), bind(api.PullReviewRequestOptions{}), repo.CreateReviewRequests) + m.Combo("/requested_reviewers", reqToken(auth_model.AccessTokenScopeRepo)). + Delete(bind(api.PullReviewRequestOptions{}), repo.DeleteReviewRequests). + Post(bind(api.PullReviewRequestOptions{}), repo.CreateReviewRequests) }) }, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo()) m.Group("/statuses", func() { m.Combo("/{sha}").Get(repo.GetCommitStatuses). - Post(reqToken(), reqRepoWriter(unit.TypeCode), bind(api.CreateStatusOption{}), repo.NewCommitStatus) + Post(reqToken(auth_model.AccessTokenScopeRepoStatus), reqRepoWriter(unit.TypeCode), bind(api.CreateStatusOption{}), repo.NewCommitStatus) }, reqRepoReader(unit.TypeCode)) m.Group("/commits", func() { m.Get("", context.ReferencesGitRepo(), repo.GetAllCommits) @@ -1062,7 +1096,7 @@ func Routes(ctx gocontext.Context) *web.Route { m.Get("/tags/{sha}", repo.GetAnnotatedTag) m.Get("/notes/{sha}", repo.GetNote) }, context.ReferencesGitRepo(true), reqRepoReader(unit.TypeCode)) - m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(), bind(api.ApplyDiffPatchFileOptions{}), repo.ApplyDiffPatch) + m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(auth_model.AccessTokenScopeRepo), bind(api.ApplyDiffPatchFileOptions{}), repo.ApplyDiffPatch) m.Group("/contents", func() { m.Get("", repo.GetContentsList) m.Get("/*", repo.GetContents) @@ -1070,15 +1104,15 @@ func Routes(ctx gocontext.Context) *web.Route { m.Post("", bind(api.CreateFileOptions{}), reqRepoBranchWriter, repo.CreateFile) m.Put("", bind(api.UpdateFileOptions{}), reqRepoBranchWriter, repo.UpdateFile) m.Delete("", bind(api.DeleteFileOptions{}), reqRepoBranchWriter, repo.DeleteFile) - }, reqToken()) + }, reqToken(auth_model.AccessTokenScopeRepo)) }, reqRepoReader(unit.TypeCode)) m.Get("/signing-key.gpg", misc.SigningKey) m.Group("/topics", func() { m.Combo("").Get(repo.ListTopics). - Put(reqToken(), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics) + Put(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics) m.Group("/{topic}", func() { - m.Combo("").Put(reqToken(), repo.AddTopic). - Delete(reqToken(), repo.DeleteTopic) + m.Combo("").Put(reqToken(auth_model.AccessTokenScopeRepo), repo.AddTopic). + Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteTopic) }, reqAdmin()) }, reqAnyRepoReader()) m.Get("/issue_templates", context.ReferencesGitRepo(), repo.GetIssueTemplates) @@ -1089,49 +1123,49 @@ func Routes(ctx gocontext.Context) *web.Route { // NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs m.Group("/packages/{username}", func() { m.Group("/{type}/{name}/{version}", func() { - m.Get("", packages.GetPackage) - m.Delete("", reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage) - m.Get("/files", packages.ListPackageFiles) + m.Get("", reqToken(auth_model.AccessTokenScopeReadPackage), packages.GetPackage) + m.Delete("", reqToken(auth_model.AccessTokenScopeDeletePackage), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage) + m.Get("/files", reqToken(auth_model.AccessTokenScopeReadPackage), packages.ListPackageFiles) }) - m.Get("/", packages.ListPackages) + m.Get("/", reqToken(auth_model.AccessTokenScopeReadPackage), packages.ListPackages) }, context_service.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead)) // Organizations - m.Get("/user/orgs", reqToken(), org.ListMyOrgs) + m.Get("/user/orgs", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListMyOrgs) m.Group("/users/{username}/orgs", func() { - m.Get("", org.ListUserOrgs) - m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListUserOrgs) + m.Get("/{org}/permissions", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetUserOrgsPermissions) }, context_service.UserAssignmentAPI()) - m.Post("/orgs", reqToken(), bind(api.CreateOrgOption{}), org.Create) - m.Get("/orgs", org.GetAll) + m.Post("/orgs", reqToken(auth_model.AccessTokenScopeWriteOrg), bind(api.CreateOrgOption{}), org.Create) + m.Get("/orgs", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetAll) m.Group("/orgs/{org}", func() { - m.Combo("").Get(org.Get). - Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit). - Delete(reqToken(), reqOrgOwnership(), org.Delete) - m.Combo("/repos").Get(user.ListOrgRepos). - Post(reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.Get). + Patch(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.Delete) + m.Combo("/repos").Get(reqToken(auth_model.AccessTokenScopeReadOrg), user.ListOrgRepos). + Post(reqToken(auth_model.AccessTokenScopeWriteOrg), bind(api.CreateRepoOption{}), repo.CreateOrgRepo) m.Group("/members", func() { - m.Get("", org.ListMembers) - m.Combo("/{username}").Get(org.IsMember). - Delete(reqToken(), reqOrgOwnership(), org.DeleteMember) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListMembers) + m.Combo("/{username}").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.IsMember). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.DeleteMember) }) m.Group("/public_members", func() { - m.Get("", org.ListPublicMembers) - m.Combo("/{username}").Get(org.IsPublicMember). - Put(reqToken(), reqOrgMembership(), org.PublicizeMember). - Delete(reqToken(), reqOrgMembership(), org.ConcealMember) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListPublicMembers) + m.Combo("/{username}").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.IsPublicMember). + Put(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgMembership(), org.PublicizeMember). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgMembership(), org.ConcealMember) }) m.Group("/teams", func() { - m.Get("", org.ListTeams) - m.Post("", reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam) - m.Get("/search", org.SearchTeam) - }, reqToken(), reqOrgMembership()) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListTeams) + m.Post("", reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam) + m.Get("/search", reqToken(auth_model.AccessTokenScopeReadOrg), org.SearchTeam) + }, reqOrgMembership()) m.Group("/labels", func() { - m.Get("", org.ListLabels) - m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel) - m.Combo("/{id}").Get(org.GetLabel). - Patch(reqToken(), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel). - Delete(reqToken(), reqOrgOwnership(), org.DeleteLabel) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListLabels) + m.Post("", reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel) + m.Combo("/{id}").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetLabel). + Patch(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.DeleteLabel) }) m.Group("/hooks", func() { m.Combo("").Get(org.ListHooks). @@ -1139,27 +1173,27 @@ func Routes(ctx gocontext.Context) *web.Route { m.Combo("/{id}").Get(org.GetHook). Patch(bind(api.EditHookOption{}), org.EditHook). Delete(org.DeleteHook) - }, reqToken(), reqOrgOwnership(), reqWebhooksEnabled()) + }, reqToken(auth_model.AccessTokenScopeAdminOrgHook), reqOrgOwnership(), reqWebhooksEnabled()) }, orgAssignment(true)) m.Group("/teams/{teamid}", func() { - m.Combo("").Get(org.GetTeam). - Patch(reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam). - Delete(reqOrgOwnership(), org.DeleteTeam) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeam). + Patch(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.DeleteTeam) m.Group("/members", func() { - m.Get("", org.GetTeamMembers) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamMembers) m.Combo("/{username}"). - Get(org.GetTeamMember). - Put(reqOrgOwnership(), org.AddTeamMember). - Delete(reqOrgOwnership(), org.RemoveTeamMember) + Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamMember). + Put(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.AddTeamMember). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.RemoveTeamMember) }) m.Group("/repos", func() { - m.Get("", org.GetTeamRepos) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamRepos) m.Combo("/{org}/{reponame}"). - Put(org.AddTeamRepository). - Delete(org.RemoveTeamRepository). - Get(org.GetTeamRepo) + Put(reqToken(auth_model.AccessTokenScopeWriteOrg), org.AddTeamRepository). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), org.RemoveTeamRepository). + Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamRepo) }) - }, orgAssignment(false, true), reqToken(), reqTeamMembership()) + }, orgAssignment(false, true), reqToken(""), reqTeamMembership()) m.Group("/admin", func() { m.Group("/cron", func() { @@ -1187,7 +1221,7 @@ func Routes(ctx gocontext.Context) *web.Route { m.Post("/{username}/{reponame}", admin.AdoptRepository) m.Delete("/{username}/{reponame}", admin.DeleteUnadoptedRepository) }) - }, reqToken(), reqSiteAdmin()) + }, reqToken(auth_model.AccessTokenScopeSudo), reqSiteAdmin()) m.Group("/topics", func() { m.Get("/search", repo.TopicSearch) diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go index eaa02588ca5c..1f71e81785a9 100644 --- a/routers/web/admin/config.go +++ b/routers/web/admin/config.go @@ -5,9 +5,11 @@ package admin import ( + "fmt" "net/http" "net/url" "os" + "strconv" "strings" system_model "code.gitea.io/gitea/models/system" @@ -201,6 +203,16 @@ func ChangeConfig(ctx *context.Context) { value := ctx.FormString("value") version := ctx.FormInt("version") + if check, ok := changeConfigChecks[key]; ok { + if err := check(ctx, value); err != nil { + log.Warn("refused to set setting: %v", err) + ctx.JSON(http.StatusOK, map[string]string{ + "err": ctx.Tr("admin.config.set_setting_failed", key), + }) + return + } + } + if err := system_model.SetSetting(&system_model.Setting{ SettingKey: key, SettingValue: value, @@ -217,3 +229,18 @@ func ChangeConfig(ctx *context.Context) { "version": version + 1, }) } + +var changeConfigChecks = map[string]func(ctx *context.Context, newValue string) error{ + system_model.KeyPictureDisableGravatar: func(_ *context.Context, newValue string) error { + if v, _ := strconv.ParseBool(newValue); setting.OfflineMode && !v { + return fmt.Errorf("%q should be true when OFFLINE_MODE is true", system_model.KeyPictureDisableGravatar) + } + return nil + }, + system_model.KeyPictureEnableFederatedAvatar: func(_ *context.Context, newValue string) error { + if v, _ := strconv.ParseBool(newValue); setting.OfflineMode && v { + return fmt.Errorf("%q cannot be false when OFFLINE_MODE is true", system_model.KeyPictureEnableFederatedAvatar) + } + return nil + }, +} diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go index 23c215738d04..b66806ff2de0 100644 --- a/routers/web/user/setting/applications.go +++ b/routers/web/user/setting/applications.go @@ -42,9 +42,15 @@ func ApplicationsPost(ctx *context.Context) { return } + scope, err := form.GetScope() + if err != nil { + ctx.ServerError("GetScope", err) + return + } t := &auth_model.AccessToken{ - UID: ctx.Doer.ID, - Name: form.Name, + UID: ctx.Doer.ID, + Name: form.Name, + Scope: scope, } exist, err := auth_model.AccessTokenByNameExists(t) diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index c0a8250e9547..1be78b85c5ef 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -59,6 +59,8 @@ func (o *OAuth2) Name() string { } // userIDFromToken returns the user id corresponding to the OAuth token. +// It will set 'IsApiToken' to true if the token is an API token and +// set 'ApiTokenScope' to the scope of the access token func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { _ = req.ParseForm() @@ -86,6 +88,7 @@ func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { uid := CheckOAuthAccessToken(tokenSHA) if uid != 0 { store.GetData()["IsApiToken"] = true + store.GetData()["ApiTokenScope"] = auth_model.AccessTokenScopeAll // fallback to all } return uid } @@ -101,6 +104,7 @@ func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { log.Error("UpdateAccessToken: %v", err) } store.GetData()["IsApiToken"] = true + store.GetData()["ApiTokenScope"] = t.Scope return t.UID } diff --git a/services/convert/pull.go b/services/convert/pull.go index db0add6cdee7..cdf72e78057f 100644 --- a/services/convert/pull.go +++ b/services/convert/pull.go @@ -88,6 +88,10 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u }, } + if pr.Issue.ClosedUnix != 0 { + apiPullRequest.Closed = pr.Issue.ClosedUnix.AsTimePtr() + } + gitRepo, err := git.OpenRepository(ctx, pr.BaseRepo.RepoPath()) if err != nil { log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RepoPath(), err) diff --git a/services/forms/user_form.go b/services/forms/user_form.go index bbea58310a40..285bc398b26c 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -9,6 +9,7 @@ import ( "net/http" "strings" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" @@ -377,7 +378,8 @@ func (f *AddSecretForm) Validate(req *http.Request, errs binding.Errors) binding // NewAccessTokenForm form for creating access token type NewAccessTokenForm struct { - Name string `binding:"Required;MaxSize(255)"` + Name string `binding:"Required;MaxSize(255)"` + Scope []string } // Validate validates the fields @@ -386,6 +388,12 @@ func (f *NewAccessTokenForm) Validate(req *http.Request, errs binding.Errors) bi return middleware.Validate(errs, ctx.Data, f, ctx.Locale) } +func (f *NewAccessTokenForm) GetScope() (auth_model.AccessTokenScope, error) { + scope := strings.Join(f.Scope, ",") + s, err := auth_model.AccessTokenScope(scope).Normalize() + return s, err +} + // EditOAuth2ApplicationForm form for editing oauth2 applications type EditOAuth2ApplicationForm struct { Name string `binding:"Required;MaxSize(255)" form:"application_name"` diff --git a/services/forms/user_form_test.go b/services/forms/user_form_test.go index 463b39d0bf07..225686f0fea0 100644 --- a/services/forms/user_form_test.go +++ b/services/forms/user_form_test.go @@ -4,8 +4,10 @@ package forms import ( + "strconv" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/setting" "github.com/stretchr/testify/assert" @@ -83,3 +85,28 @@ func TestRegisterForm_IsDomainAllowed_BlocklistedEmail(t *testing.T) { assert.Equal(t, v.valid, form.IsEmailDomainAllowed()) } } + +func TestNewAccessTokenForm_GetScope(t *testing.T) { + tests := []struct { + form NewAccessTokenForm + scope auth_model.AccessTokenScope + expectedErr error + }{ + { + form: NewAccessTokenForm{Name: "test", Scope: []string{"repo"}}, + scope: "repo", + }, + { + form: NewAccessTokenForm{Name: "test", Scope: []string{"repo", "user"}}, + scope: "repo,user", + }, + } + + for i, test := range tests { + t.Run(strconv.Itoa(i), func(t *testing.T) { + scope, err := test.form.GetScope() + assert.Equal(t, test.expectedErr, err) + assert.Equal(t, test.scope, scope) + }) + } +} diff --git a/services/migrations/gitbucket.go b/services/migrations/gitbucket.go index 65c138ed04a2..cc3d4fc93674 100644 --- a/services/migrations/gitbucket.go +++ b/services/migrations/gitbucket.go @@ -33,10 +33,14 @@ func (f *GitBucketDownloaderFactory) New(ctx context.Context, opts base.MigrateO return nil, err } - baseURL := u.Scheme + "://" + u.Host fields := strings.Split(u.Path, "/") - oldOwner := fields[1] - oldName := strings.TrimSuffix(fields[2], ".git") + if len(fields) < 2 { + return nil, fmt.Errorf("invalid path: %s", u.Path) + } + baseURL := u.Scheme + "://" + u.Host + strings.TrimSuffix(strings.Join(fields[:len(fields)-2], "/"), "/git") + + oldOwner := fields[len(fields)-2] + oldName := strings.TrimSuffix(fields[len(fields)-1], ".git") log.Trace("Create GitBucket downloader. BaseURL: %s RepoOwner: %s RepoName: %s", baseURL, oldOwner, oldName) return NewGitBucketDownloader(ctx, baseURL, opts.AuthUsername, opts.AuthPassword, opts.AuthToken, oldOwner, oldName), nil @@ -71,6 +75,7 @@ func (g *GitBucketDownloader) ColorFormat(s fmt.State) { func NewGitBucketDownloader(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GitBucketDownloader { githubDownloader := NewGithubDownloaderV3(ctx, baseURL, userName, password, token, repoOwner, repoName) githubDownloader.SkipReactions = true + githubDownloader.SkipReviews = true return &GitBucketDownloader{ githubDownloader, } diff --git a/services/migrations/github.go b/services/migrations/github.go index 48dd90323d5a..d34ad13b95a7 100644 --- a/services/migrations/github.go +++ b/services/migrations/github.go @@ -76,6 +76,7 @@ type GithubDownloaderV3 struct { curClientIdx int maxPerPage int SkipReactions bool + SkipReviews bool } // NewGithubDownloaderV3 creates a github Downloader via github v3 API @@ -809,6 +810,9 @@ func (g *GithubDownloaderV3) convertGithubReviewComments(cs []*github.PullReques // GetReviews returns pull requests review func (g *GithubDownloaderV3) GetReviews(reviewable base.Reviewable) ([]*base.Review, error) { allReviews := make([]*base.Review, 0, g.maxPerPage) + if g.SkipReviews { + return allReviews, nil + } opt := &github.ListOptions{ PerPage: g.maxPerPage, } diff --git a/services/pull/pull.go b/services/pull/pull.go index afb0fa244244..08f70a5e4ef0 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -240,7 +240,7 @@ func checkForInvalidation(ctx context.Context, requests issues_model.PullRequest } go func() { // FIXME: graceful: We need to tell the manager we're doing something... - err := requests.InvalidateCodeComments(ctx, doer, gitRepo, branch) + err := InvalidateCodeComments(ctx, requests, doer, gitRepo, branch) if err != nil { log.Error("PullRequestList.InvalidateCodeComments: %v", err) } diff --git a/services/pull/review.go b/services/pull/review.go index 67a10d7aad61..ca386ca6b027 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -23,6 +23,53 @@ import ( issue_service "code.gitea.io/gitea/services/issue" ) +var notEnoughLines = regexp.MustCompile(`fatal: file .* has only \d+ lines?`) + +// checkInvalidation checks if the line of code comment got changed by another commit. +// If the line got changed the comment is going to be invalidated. +func checkInvalidation(ctx context.Context, c *issues_model.Comment, doer *user_model.User, repo *git.Repository, branch string) error { + // FIXME differentiate between previous and proposed line + commit, err := repo.LineBlame(branch, repo.Path, c.TreePath, uint(c.UnsignedLine())) + if err != nil && (strings.Contains(err.Error(), "fatal: no such path") || notEnoughLines.MatchString(err.Error())) { + c.Invalidated = true + return issues_model.UpdateCommentInvalidate(ctx, c) + } + if err != nil { + return err + } + if c.CommitSHA != "" && c.CommitSHA != commit.ID.String() { + c.Invalidated = true + return issues_model.UpdateCommentInvalidate(ctx, c) + } + return nil +} + +// InvalidateCodeComments will lookup the prs for code comments which got invalidated by change +func InvalidateCodeComments(ctx context.Context, prs issues_model.PullRequestList, doer *user_model.User, repo *git.Repository, branch string) error { + if len(prs) == 0 { + return nil + } + issueIDs := prs.GetIssueIDs() + var codeComments []*issues_model.Comment + + if err := db.Find(ctx, &issues_model.FindCommentsOptions{ + ListOptions: db.ListOptions{ + ListAll: true, + }, + Type: issues_model.CommentTypeCode, + Invalidated: util.OptionalBoolFalse, + IssueIDs: issueIDs, + }, &codeComments); err != nil { + return fmt.Errorf("find code comments: %v", err) + } + for _, comment := range codeComments { + if err := checkInvalidation(ctx, comment, doer, repo, branch); err != nil { + return err + } + } + return nil +} + // CreateCodeComment creates a comment on the code line func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, issue *issues_model.Issue, line int64, content, treePath string, isReview bool, replyReviewID int64, latestCommitID string) (*issues_model.Comment, error) { var ( @@ -114,8 +161,6 @@ func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git. return comment, nil } -var notEnoughLines = regexp.MustCompile(`exit status 128 - fatal: file .* has only \d+ lines?`) - // createCodeComment creates a plain code comment at the specified line / path func createCodeComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, content, treePath string, line, reviewID int64) (*issues_model.Comment, error) { var commitID, patch string diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl index 3fa0f8e7aca8..4d3e08a5977e 100644 --- a/templates/base/footer.tmpl +++ b/templates/base/footer.tmpl @@ -22,7 +22,7 @@ {{end}} {{end}} - + {{template "custom/footer" .}} diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl index e05cebb0e4e8..c108f20b5b32 100644 --- a/templates/user/settings/applications.tmpl +++ b/templates/user/settings/applications.tmpl @@ -41,6 +41,207 @@ +
+ + {{.locale.Tr "settings.select_scopes"}} + +
+
+ + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + +
+
+
diff --git a/tests/integration/api_admin_org_test.go b/tests/integration/api_admin_org_test.go index 05825eff316e..89617f7a2c47 100644 --- a/tests/integration/api_admin_org_test.go +++ b/tests/integration/api_admin_org_test.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" @@ -20,7 +21,7 @@ import ( func TestAPIAdminOrgCreate(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeSudo) org := api.CreateOrgOption{ UserName: "user2_org", @@ -54,7 +55,7 @@ func TestAPIAdminOrgCreate(t *testing.T) { func TestAPIAdminOrgCreateBadVisibility(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeSudo) org := api.CreateOrgOption{ UserName: "user2_org", diff --git a/tests/integration/api_admin_test.go b/tests/integration/api_admin_test.go index 53952210fd95..b608c26f6ee5 100644 --- a/tests/integration/api_admin_test.go +++ b/tests/integration/api_admin_test.go @@ -9,6 +9,7 @@ import ( "testing" asymkey_model "code.gitea.io/gitea/models/asymkey" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/json" @@ -24,7 +25,7 @@ func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) { session := loginUser(t, "user1") keyOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", keyOwner.Name, token) req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n", @@ -51,7 +52,7 @@ func TestAPIAdminDeleteMissingSSHKey(t *testing.T) { defer tests.PrepareTestEnv(t)() // user1 is an admin user - token := getUserToken(t, "user1") + token := getUserToken(t, "user1", auth_model.AccessTokenScopeSudo) req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d?token=%s", unittest.NonexistentID, token) MakeRequest(t, req, http.StatusNotFound) } @@ -60,7 +61,7 @@ func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" normalUsername := "user2" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", adminUsername, token) req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ @@ -81,7 +82,7 @@ func TestAPISudoUser(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" normalUsername := "user2" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/user?sudo=%s&token=%s", normalUsername, token) req := NewRequest(t, "GET", urlStr) @@ -97,7 +98,7 @@ func TestAPISudoUserForbidden(t *testing.T) { adminUsername := "user1" normalUsername := "user2" - token := getUserToken(t, normalUsername) + token := getUserToken(t, normalUsername, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/user?sudo=%s&token=%s", adminUsername, token) req := NewRequest(t, "GET", urlStr) MakeRequest(t, req, http.StatusForbidden) @@ -106,7 +107,7 @@ func TestAPISudoUserForbidden(t *testing.T) { func TestAPIListUsers(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/admin/users?token=%s", token) req := NewRequest(t, "GET", urlStr) @@ -142,7 +143,7 @@ func TestAPIListUsersNonAdmin(t *testing.T) { func TestAPICreateUserInvalidEmail(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/admin/users?token=%s", token) req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ "email": "invalid_email@domain.com\r\n", @@ -160,7 +161,7 @@ func TestAPICreateUserInvalidEmail(t *testing.T) { func TestAPICreateAndDeleteUser(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) req := NewRequestWithValues( t, @@ -186,7 +187,7 @@ func TestAPICreateAndDeleteUser(t *testing.T) { func TestAPIEditUser(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/admin/users/%s?token=%s", "user2", token) req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ @@ -228,7 +229,7 @@ func TestAPIEditUser(t *testing.T) { func TestAPICreateRepoForUser(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) req := NewRequestWithJSON( t, diff --git a/tests/integration/api_branch_test.go b/tests/integration/api_branch_test.go index 278edfbf9c1f..0d4a750a29d2 100644 --- a/tests/integration/api_branch_test.go +++ b/tests/integration/api_branch_test.go @@ -8,6 +8,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -15,7 +16,7 @@ import ( ) func testAPIGetBranch(t *testing.T, branchName string, exists bool) { - token := getUserToken(t, "user2") + token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branches/%s?token=%s", branchName, token) resp := MakeRequest(t, req, NoExpectedStatus) if !exists { @@ -31,7 +32,7 @@ func testAPIGetBranch(t *testing.T, branchName string, exists bool) { } func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { - token := getUserToken(t, "user2") + token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token) resp := MakeRequest(t, req, expectedHTTPStatus) @@ -43,7 +44,7 @@ func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPSta } func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { - token := getUserToken(t, "user2") + token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branch_protections?token="+token, &api.BranchProtection{ RuleName: branchName, }) @@ -57,7 +58,7 @@ func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTP } func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.BranchProtection, expectedHTTPStatus int) { - token := getUserToken(t, "user2") + token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, "PATCH", "/api/v1/repos/user2/repo1/branch_protections/"+branchName+"?token="+token, body) resp := MakeRequest(t, req, expectedHTTPStatus) @@ -69,13 +70,13 @@ func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.Bran } func testAPIDeleteBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { - token := getUserToken(t, "user2") + token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token) MakeRequest(t, req, expectedHTTPStatus) } func testAPIDeleteBranch(t *testing.T, branchName string, expectedHTTPStatus int) { - token := getUserToken(t, "user2") + token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branches/%s?token=%s", branchName, token) MakeRequest(t, req, expectedHTTPStatus) } @@ -101,7 +102,7 @@ func TestAPICreateBranch(t *testing.T) { func testAPICreateBranches(t *testing.T, giteaURL *url.URL) { username := "user2" - ctx := NewAPITestContext(t, username, "my-noo-repo") + ctx := NewAPITestContext(t, username, "my-noo-repo", auth_model.AccessTokenScopeRepo) giteaURL.Path = ctx.GitPath() t.Run("CreateRepo", doAPICreateRepository(ctx, false)) @@ -149,7 +150,7 @@ func testAPICreateBranches(t *testing.T, giteaURL *url.URL) { } func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBranch, newBranch string, status int) bool { - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, "POST", "/api/v1/repos/"+user+"/"+repo+"/branches?token="+token, &api.CreateBranchRepoOption{ BranchName: newBranch, OldBranchName: oldBranch, diff --git a/tests/integration/api_comment_attachment_test.go b/tests/integration/api_comment_attachment_test.go index b23db53d2810..1f916ffa15ce 100644 --- a/tests/integration/api_comment_attachment_test.go +++ b/tests/integration/api_comment_attachment_test.go @@ -12,6 +12,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -81,7 +82,7 @@ func TestAPICreateCommentAttachment(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets?token=%s", repoOwner.Name, repo.Name, comment.ID, token) @@ -120,7 +121,7 @@ func TestAPIEditCommentAttachment(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, attachment.ID, token) req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ @@ -143,7 +144,7 @@ func TestAPIDeleteCommentAttachment(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, attachment.ID, token) diff --git a/tests/integration/api_comment_test.go b/tests/integration/api_comment_test.go index fb2d41223e7e..cc7712e54818 100644 --- a/tests/integration/api_comment_test.go +++ b/tests/integration/api_comment_test.go @@ -9,6 +9,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -75,8 +76,9 @@ func TestAPIListIssueComments(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/comments", - repoOwner.Name, repo.Name, issue.Index) + token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/comments?token=%s", + repoOwner.Name, repo.Name, issue.Index, token) resp := MakeRequest(t, req, http.StatusOK) var comments []*api.Comment @@ -94,7 +96,7 @@ func TestAPICreateComment(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - token := getUserToken(t, repoOwner.Name) + token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments?token=%s", repoOwner.Name, repo.Name, issue.Index, token) req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ @@ -116,7 +118,7 @@ func TestAPIGetComment(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: comment.Issue.RepoID}) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - token := getUserToken(t, repoOwner.Name) + token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d", repoOwner.Name, repo.Name, comment.ID) MakeRequest(t, req, http.StatusOK) req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, token) @@ -144,7 +146,7 @@ func TestAPIEditComment(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - token := getUserToken(t, repoOwner.Name) + token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, token) req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ @@ -168,7 +170,7 @@ func TestAPIDeleteComment(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - token := getUserToken(t, repoOwner.Name) + token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/comments/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, token) MakeRequest(t, req, http.StatusNoContent) diff --git a/tests/integration/api_gpg_keys_test.go b/tests/integration/api_gpg_keys_test.go index 162a5a4fd55e..f66961786fff 100644 --- a/tests/integration/api_gpg_keys_test.go +++ b/tests/integration/api_gpg_keys_test.go @@ -9,6 +9,7 @@ import ( "strconv" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -20,7 +21,8 @@ type makeRequestFunc func(testing.TB, *http.Request, int) *httptest.ResponseReco func TestGPGKeys(t *testing.T) { defer tests.PrepareTestEnv(t)() session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) + tokenWithGPGKeyScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminGPGKey, auth_model.AccessTokenScopeRepo) tt := []struct { name string @@ -34,6 +36,10 @@ func TestGPGKeys(t *testing.T) { }, { name: "LoggedAsUser2", makeRequest: session.MakeRequest, token: token, + results: []int{http.StatusForbidden, http.StatusOK, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden}, + }, + { + name: "LoggedAsUser2WithScope", makeRequest: session.MakeRequest, token: tokenWithGPGKeyScope, results: []int{http.StatusOK, http.StatusOK, http.StatusNotFound, http.StatusNoContent, http.StatusUnprocessableEntity, http.StatusNotFound, http.StatusCreated, http.StatusNotFound, http.StatusCreated}, }, } @@ -73,7 +79,7 @@ func TestGPGKeys(t *testing.T) { t.Run("CheckState", func(t *testing.T) { var keys []*api.GPGKey - req := NewRequest(t, "GET", "/api/v1/user/gpg_keys?token="+token) // GET all keys + req := NewRequest(t, "GET", "/api/v1/user/gpg_keys?token="+tokenWithGPGKeyScope) // GET all keys resp := MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &keys) assert.Len(t, keys, 1) @@ -89,7 +95,7 @@ func TestGPGKeys(t *testing.T) { assert.Empty(t, subKey.Emails) var key api.GPGKey - req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(primaryKey1.ID, 10)+"?token="+token) // Primary key 1 + req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(primaryKey1.ID, 10)+"?token="+tokenWithGPGKeyScope) // Primary key 1 resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &key) assert.EqualValues(t, "38EA3BCED732982C", key.KeyID) @@ -97,7 +103,7 @@ func TestGPGKeys(t *testing.T) { assert.EqualValues(t, "user2@example.com", key.Emails[0].Email) assert.True(t, key.Emails[0].Verified) - req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(subKey.ID, 10)+"?token="+token) // Subkey of 38EA3BCED732982C + req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(subKey.ID, 10)+"?token="+tokenWithGPGKeyScope) // Subkey of 38EA3BCED732982C resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &key) assert.EqualValues(t, "70D7C694D17D03AD", key.KeyID) diff --git a/tests/integration/api_helper_for_declarative_test.go b/tests/integration/api_helper_for_declarative_test.go index dbfe502ec1d2..3524ce9834ad 100644 --- a/tests/integration/api_helper_for_declarative_test.go +++ b/tests/integration/api_helper_for_declarative_test.go @@ -13,6 +13,7 @@ import ( "testing" "time" + "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/json" @@ -31,9 +32,9 @@ type APITestContext struct { ExpectedCode int } -func NewAPITestContext(t *testing.T, username, reponame string) APITestContext { +func NewAPITestContext(t *testing.T, username, reponame string, scope ...auth.AccessTokenScope) APITestContext { session := loginUser(t, username) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, scope...) return APITestContext{ Session: session, Token: token, diff --git a/tests/integration/api_httpsig_test.go b/tests/integration/api_httpsig_test.go index 881bb45ca497..57f83490dcc9 100644 --- a/tests/integration/api_httpsig_test.go +++ b/tests/integration/api_httpsig_test.go @@ -10,6 +10,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -52,7 +53,7 @@ func TestHTTPSigPubKey(t *testing.T) { // Add our public key to user1 defer tests.PrepareTestEnv(t)() session := loginUser(t, "user1") - token := url.QueryEscape(getTokenForLoggedInUser(t, session)) + token := url.QueryEscape(getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminPublicKey, auth_model.AccessTokenScopeSudo)) keysURL := fmt.Sprintf("/api/v1/user/keys?token=%s", token) keyType := "ssh-rsa" keyContent := "AAAAB3NzaC1yc2EAAAADAQABAAABAQCqOZB5vkRvXFXups1/0StDRdG8plbNSwsWEnNnP4Bvurxa0+z3W9B8GLKnDiLw5MbpbMNyBlpXw13GfuIeciy10DWTz0xUbiy3J3KabCaT36asIw2y7k6Z0jL0UBnrVENwq5/lUbZYqSZ4rRU744wkhh8TULpzM14npQCZwg6aEbG+MwjzddQ72fR+3BPBrKn5dTmmu8rH99O+U+Nuto81Tg7PA+NUupcHOmhdiEGq49plgVFXK98Vks5tiybL4GuzFyWgyX73Dg/QBMn2eMHt1EMv5Gs3i6GFhKKGo4rjDi9qI6PX5oDR4LTNe6cR8td8YhVD8WFZwLLl/vaYyIqd" diff --git a/tests/integration/api_issue_attachment_test.go b/tests/integration/api_issue_attachment_test.go index 0558dda56a14..b4d6dab42a18 100644 --- a/tests/integration/api_issue_attachment_test.go +++ b/tests/integration/api_issue_attachment_test.go @@ -12,6 +12,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -72,7 +73,7 @@ func TestAPICreateIssueAttachment(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets?token=%s", repoOwner.Name, repo.Name, issue.Index, token) @@ -110,7 +111,7 @@ func TestAPIEditIssueAttachment(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets/%d?token=%s", repoOwner.Name, repo.Name, issue.Index, attachment.ID, token) req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ @@ -132,7 +133,7 @@ func TestAPIDeleteIssueAttachment(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets/%d?token=%s", repoOwner.Name, repo.Name, issue.Index, attachment.ID, token) diff --git a/tests/integration/api_issue_label_test.go b/tests/integration/api_issue_label_test.go index 6f0fd879135e..18240159834a 100644 --- a/tests/integration/api_issue_label_test.go +++ b/tests/integration/api_issue_label_test.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -24,7 +25,7 @@ func TestAPIModifyLabels(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/labels?token=%s", owner.Name, repo.Name, token) // CreateLabel @@ -96,7 +97,7 @@ func TestAPIAddIssueLabels(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels?token=%s", repo.OwnerName, repo.Name, issue.Index, token) req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueLabelsOption{ @@ -119,7 +120,7 @@ func TestAPIReplaceIssueLabels(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels?token=%s", owner.Name, repo.Name, issue.Index, token) req := NewRequestWithJSON(t, "PUT", urlStr, &api.IssueLabelsOption{ @@ -143,7 +144,7 @@ func TestAPIModifyOrgLabels(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) user := "user1" session := loginUser(t, user) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeAdminOrg) urlStr := fmt.Sprintf("/api/v1/orgs/%s/labels?token=%s", owner.Name, token) // CreateLabel diff --git a/tests/integration/api_issue_milestone_test.go b/tests/integration/api_issue_milestone_test.go index 60766e10fdbe..cbce795bc9a6 100644 --- a/tests/integration/api_issue_milestone_test.go +++ b/tests/integration/api_issue_milestone_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -28,7 +29,7 @@ func TestAPIIssuesMilestone(t *testing.T) { assert.Equal(t, structs.StateOpen, milestone.State()) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // update values of issue milestoneState := "closed" diff --git a/tests/integration/api_issue_reaction_test.go b/tests/integration/api_issue_reaction_test.go index 4e2ae3d57dc7..76140d751124 100644 --- a/tests/integration/api_issue_reaction_test.go +++ b/tests/integration/api_issue_reaction_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" @@ -28,7 +29,7 @@ func TestAPIIssuesReactions(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/reactions?token=%s", @@ -87,7 +88,7 @@ func TestAPICommentReactions(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) diff --git a/tests/integration/api_issue_stopwatch_test.go b/tests/integration/api_issue_stopwatch_test.go index d1a3e86fdad2..a8a832414d35 100644 --- a/tests/integration/api_issue_stopwatch_test.go +++ b/tests/integration/api_issue_stopwatch_test.go @@ -7,6 +7,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -25,7 +26,7 @@ func TestAPIListStopWatches(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/user/stopwatches?token=%s", token) resp := MakeRequest(t, req, http.StatusOK) var apiWatches []*api.StopWatch @@ -51,7 +52,7 @@ func TestAPIStopStopWatches(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/issues/%d/stopwatch/stop?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) MakeRequest(t, req, http.StatusCreated) @@ -67,7 +68,7 @@ func TestAPICancelStopWatches(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/stopwatch/delete?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) MakeRequest(t, req, http.StatusNoContent) @@ -83,7 +84,7 @@ func TestAPIStartStopWatches(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/issues/%d/stopwatch/start?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) MakeRequest(t, req, http.StatusCreated) diff --git a/tests/integration/api_issue_subscription_test.go b/tests/integration/api_issue_subscription_test.go index a32b51e6fcb9..473e72075409 100644 --- a/tests/integration/api_issue_subscription_test.go +++ b/tests/integration/api_issue_subscription_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -30,7 +31,7 @@ func TestAPIIssueSubscriptions(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue1.PosterID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) testSubscription := func(issue *issues_model.Issue, isWatching bool) { issueRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go index 2074bbee7ca7..2f27978a371d 100644 --- a/tests/integration/api_issue_test.go +++ b/tests/integration/api_issue_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -29,7 +30,7 @@ func TestAPIListIssues(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner.Name, repo.Name)) link.RawQuery = url.Values{"token": {token}, "state": {"all"}}.Encode() @@ -80,7 +81,7 @@ func TestAPICreateIssue(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token) req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{ Body: body, @@ -116,7 +117,7 @@ func TestAPIEditIssue(t *testing.T) { assert.Equal(t, api.StateOpen, issueBefore.State()) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // update values of issue issueState := "closed" diff --git a/tests/integration/api_issue_tracked_time_test.go b/tests/integration/api_issue_tracked_time_test.go index eda4150f8c47..7d9c78547401 100644 --- a/tests/integration/api_issue_tracked_time_test.go +++ b/tests/integration/api_issue_tracked_time_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" @@ -27,7 +28,7 @@ func TestAPIGetTrackedTimes(t *testing.T) { assert.NoError(t, issue2.LoadRepo(db.DefaultContext)) session := loginUser(t, user2.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/times?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, token) resp := MakeRequest(t, req, http.StatusOK) @@ -70,7 +71,7 @@ func TestAPIDeleteTrackedTime(t *testing.T) { user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user2.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Deletion not allowed req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/times/%d?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, time6.ID, token) @@ -105,7 +106,7 @@ func TestAPIAddTrackedTimes(t *testing.T) { admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) session := loginUser(t, admin.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/times?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, token) diff --git a/tests/integration/api_keys_test.go b/tests/integration/api_keys_test.go index d24db2bd160f..dc25cbfc1a7c 100644 --- a/tests/integration/api_keys_test.go +++ b/tests/integration/api_keys_test.go @@ -10,6 +10,7 @@ import ( "testing" asymkey_model "code.gitea.io/gitea/models/asymkey" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -53,7 +54,7 @@ func TestCreateReadOnlyDeployKey(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", repoOwner.Name, repo.Name, token) rawKeyBody := api.CreateKeyOption{ Title: "read-only", @@ -79,7 +80,7 @@ func TestCreateReadWriteDeployKey(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", repoOwner.Name, repo.Name, token) rawKeyBody := api.CreateKeyOption{ Title: "read-write", @@ -103,7 +104,7 @@ func TestCreateUserKey(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user1"}) session := loginUser(t, "user1") - token := url.QueryEscape(getTokenForLoggedInUser(t, session)) + token := url.QueryEscape(getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminPublicKey)) keysURL := fmt.Sprintf("/api/v1/user/keys?token=%s", token) keyType := "ssh-rsa" keyContent := "AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM=" @@ -167,7 +168,7 @@ func TestCreateUserKey(t *testing.T) { // Now login as user 2 session2 := loginUser(t, "user2") - token2 := url.QueryEscape(getTokenForLoggedInUser(t, session2)) + token2 := url.QueryEscape(getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeAdminPublicKey)) // Should find key even though not ours, but we shouldn't know whose it is fingerprintURL = fmt.Sprintf("/api/v1/user/keys?token=%s&fingerprint=%s", token2, newPublicKey.Fingerprint) diff --git a/tests/integration/api_notification_test.go b/tests/integration/api_notification_test.go index cd230d6883c9..0ff13704cf69 100644 --- a/tests/integration/api_notification_test.go +++ b/tests/integration/api_notification_test.go @@ -9,6 +9,7 @@ import ( "testing" activities_model "code.gitea.io/gitea/models/activities" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -27,7 +28,7 @@ func TestAPINotification(t *testing.T) { thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) session := loginUser(t, user2.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeNotification) // -- GET /notifications -- // test filter @@ -145,7 +146,7 @@ func TestAPINotificationPUT(t *testing.T) { thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) session := loginUser(t, user2.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeNotification) // Check notifications are as expected req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?all=true&token=%s", token)) diff --git a/tests/integration/api_oauth2_apps_test.go b/tests/integration/api_oauth2_apps_test.go index d2a85992ac0d..c320efb391ed 100644 --- a/tests/integration/api_oauth2_apps_test.go +++ b/tests/integration/api_oauth2_apps_test.go @@ -8,7 +8,7 @@ import ( "net/http" "testing" - "code.gitea.io/gitea/models/auth" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" @@ -49,15 +49,15 @@ func testAPICreateOAuth2Application(t *testing.T) { assert.True(t, createdApp.ConfidentialClient) assert.NotEmpty(t, createdApp.Created) assert.EqualValues(t, appBody.RedirectURIs[0], createdApp.RedirectURIs[0]) - unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{UID: user.ID, Name: createdApp.Name}) + unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{UID: user.ID, Name: createdApp.Name}) } func testAPIListOAuth2Applications(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadApplication) - existApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ + existApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ UID: user.ID, Name: "test-app-1", RedirectURIs: []string{ @@ -80,15 +80,15 @@ func testAPIListOAuth2Applications(t *testing.T) { assert.Len(t, expectedApp.ClientID, 36) assert.Empty(t, expectedApp.ClientSecret) assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0]) - unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) + unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) } func testAPIDeleteOAuth2Application(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteApplication) - oldApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ + oldApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ UID: user.ID, Name: "test-app-1", }) @@ -97,7 +97,7 @@ func testAPIDeleteOAuth2Application(t *testing.T) { req := NewRequest(t, "DELETE", urlStr) MakeRequest(t, req, http.StatusNoContent) - unittest.AssertNotExistsBean(t, &auth.OAuth2Application{UID: oldApp.UID, Name: oldApp.Name}) + unittest.AssertNotExistsBean(t, &auth_model.OAuth2Application{UID: oldApp.UID, Name: oldApp.Name}) // Delete again will return not found req = NewRequest(t, "DELETE", urlStr) @@ -107,9 +107,9 @@ func testAPIDeleteOAuth2Application(t *testing.T) { func testAPIGetOAuth2Application(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadApplication) - existApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ + existApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ UID: user.ID, Name: "test-app-1", RedirectURIs: []string{ @@ -133,13 +133,13 @@ func testAPIGetOAuth2Application(t *testing.T) { assert.Empty(t, expectedApp.ClientSecret) assert.Len(t, expectedApp.RedirectURIs, 1) assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0]) - unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) + unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) } func testAPIUpdateOAuth2Application(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - existApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ + existApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ UID: user.ID, Name: "test-app-1", RedirectURIs: []string{ @@ -169,5 +169,5 @@ func testAPIUpdateOAuth2Application(t *testing.T) { assert.EqualValues(t, expectedApp.RedirectURIs[0], appBody.RedirectURIs[0]) assert.EqualValues(t, expectedApp.RedirectURIs[1], appBody.RedirectURIs[1]) assert.Equal(t, expectedApp.ConfidentialClient, appBody.ConfidentialClient) - unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) + unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) } diff --git a/tests/integration/api_org_test.go b/tests/integration/api_org_test.go index 0be0c170d69e..84166861a727 100644 --- a/tests/integration/api_org_test.go +++ b/tests/integration/api_org_test.go @@ -10,6 +10,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" @@ -21,7 +22,7 @@ import ( func TestAPIOrgCreate(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { - token := getUserToken(t, "user1") + token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteOrg) org := api.CreateOrgOption{ UserName: "user1_org", @@ -79,7 +80,7 @@ func TestAPIOrgEdit(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrg) org := api.EditOrgOption{ FullName: "User3 organization new full name", Description: "A new description", @@ -106,7 +107,7 @@ func TestAPIOrgEditBadVisibility(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrg) org := api.EditOrgOption{ FullName: "User3 organization new full name", Description: "A new description", @@ -126,14 +127,16 @@ func TestAPIOrgDeny(t *testing.T) { setting.Service.RequireSignInView = false }() + token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadOrg) + orgName := "user1_org" - req := NewRequestf(t, "GET", "/api/v1/orgs/%s", orgName) + req := NewRequestf(t, "GET", "/api/v1/orgs/%s?token=%s", orgName, token) MakeRequest(t, req, http.StatusNotFound) - req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", orgName) + req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token=%s", orgName, token) MakeRequest(t, req, http.StatusNotFound) - req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members", orgName) + req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members?token=%s", orgName, token) MakeRequest(t, req, http.StatusNotFound) }) } @@ -141,20 +144,23 @@ func TestAPIOrgDeny(t *testing.T) { func TestAPIGetAll(t *testing.T) { defer tests.PrepareTestEnv(t)() - req := NewRequestf(t, "GET", "/api/v1/orgs") + token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadOrg) + + req := NewRequestf(t, "GET", "/api/v1/orgs?token=%s", token) resp := MakeRequest(t, req, http.StatusOK) var apiOrgList []*api.Organization DecodeJSON(t, resp, &apiOrgList) - assert.Len(t, apiOrgList, 7) - assert.Equal(t, "org25", apiOrgList[0].FullName) - assert.Equal(t, "public", apiOrgList[0].Visibility) + // accessing with a token will return all orgs + assert.Len(t, apiOrgList, 9) + assert.Equal(t, "org25", apiOrgList[1].FullName) + assert.Equal(t, "public", apiOrgList[1].Visibility) } func TestAPIOrgSearchEmptyTeam(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { - token := getUserToken(t, "user1") + token := getUserToken(t, "user1", auth_model.AccessTokenScopeAdminOrg) orgName := "org_with_empty_team" // create org diff --git a/tests/integration/api_packages_container_test.go b/tests/integration/api_packages_container_test.go index 1dcd76a31713..39297c7d94c4 100644 --- a/tests/integration/api_packages_container_test.go +++ b/tests/integration/api_packages_container_test.go @@ -13,6 +13,7 @@ import ( "sync" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" container_model "code.gitea.io/gitea/models/packages/container" @@ -31,6 +32,8 @@ func TestPackageContainer(t *testing.T) { defer tests.PrepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage) has := func(l packages_model.PackagePropertyList, name string) bool { for _, pp := range l { @@ -558,7 +561,7 @@ func TestPackageContainer(t *testing.T) { assert.Equal(t, c.ExpectedLink, resp.Header().Get("Link")) } - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?type=container&q=%s", user.Name, image)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?type=container&q=%s&token=%s", user.Name, image, token)) resp := MakeRequest(t, req, http.StatusOK) var apiPackages []*api.Package diff --git a/tests/integration/api_packages_test.go b/tests/integration/api_packages_test.go index 8346e3bcccaa..9bca6a20ee16 100644 --- a/tests/integration/api_packages_test.go +++ b/tests/integration/api_packages_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" container_model "code.gitea.io/gitea/models/packages/container" @@ -28,7 +29,8 @@ func TestPackageAPI(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + tokenReadPackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage) + tokenDeletePackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeDeletePackage) packageName := "test-package" packageVersion := "1.0.3" @@ -42,7 +44,7 @@ func TestPackageAPI(t *testing.T) { t.Run("ListPackages", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?token=%s", user.Name, token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?token=%s", user.Name, tokenReadPackage)) resp := MakeRequest(t, req, http.StatusOK) var apiPackages []*api.Package @@ -59,10 +61,10 @@ func TestPackageAPI(t *testing.T) { t.Run("GetPackage", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) MakeRequest(t, req, http.StatusNotFound) - req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) resp := MakeRequest(t, req, http.StatusOK) var p *api.Package @@ -81,7 +83,7 @@ func TestPackageAPI(t *testing.T) { assert.NoError(t, err) // no repository link - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) resp := MakeRequest(t, req, http.StatusOK) var ap1 *api.Package @@ -91,7 +93,7 @@ func TestPackageAPI(t *testing.T) { // link to public repository assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 1)) - req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) resp = MakeRequest(t, req, http.StatusOK) var ap2 *api.Package @@ -102,7 +104,7 @@ func TestPackageAPI(t *testing.T) { // link to private repository assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 2)) - req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) resp = MakeRequest(t, req, http.StatusOK) var ap3 *api.Package @@ -116,10 +118,10 @@ func TestPackageAPI(t *testing.T) { t.Run("ListPackageFiles", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s/files?token=%s", user.Name, packageName, packageVersion, token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s/files?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) MakeRequest(t, req, http.StatusNotFound) - req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s/files?token=%s", user.Name, packageName, packageVersion, token)) + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s/files?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) resp := MakeRequest(t, req, http.StatusOK) var files []*api.PackageFile @@ -137,10 +139,10 @@ func TestPackageAPI(t *testing.T) { t.Run("DeletePackage", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenDeletePackage)) MakeRequest(t, req, http.StatusNotFound) - req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenDeletePackage)) MakeRequest(t, req, http.StatusNoContent) }) } diff --git a/tests/integration/api_pull_review_test.go b/tests/integration/api_pull_review_test.go index 4b9c60178343..cfb56724a633 100644 --- a/tests/integration/api_pull_review_test.go +++ b/tests/integration/api_pull_review_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -27,7 +28,7 @@ func TestAPIPullReview(t *testing.T) { // test ListPullReviews session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token) resp := MakeRequest(t, req, http.StatusOK) @@ -230,7 +231,7 @@ func TestAPIPullReviewRequest(t *testing.T) { // Test add Review Request session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.PullReviewRequestOptions{ Reviewers: []string{"user4@example.com", "user8"}, }) @@ -250,7 +251,7 @@ func TestAPIPullReviewRequest(t *testing.T) { // Test Remove Review Request session2 := loginUser(t, "user4") - token2 := getTokenForLoggedInUser(t, session2) + token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeRepo) req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token2), &api.PullReviewRequestOptions{ Reviewers: []string{"user4"}, diff --git a/tests/integration/api_pull_test.go b/tests/integration/api_pull_test.go index 89d39179a688..4427c610bfa4 100644 --- a/tests/integration/api_pull_test.go +++ b/tests/integration/api_pull_test.go @@ -9,6 +9,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -28,7 +29,7 @@ func TestAPIViewPulls(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - ctx := NewAPITestContext(t, "user2", repo.Name) + ctx := NewAPITestContext(t, "user2", repo.Name, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls?state=all&token="+ctx.Token, owner.Name, repo.Name) resp := ctx.Session.MakeRequest(t, req, http.StatusOK) @@ -74,7 +75,7 @@ func TestAPIMergePullWIP(t *testing.T) { assert.Contains(t, pr.Issue.Title, setting.Repository.PullRequest.WorkInProgressPrefixes[0]) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge?token=%s", owner.Name, repo.Name, pr.Index, token), &forms.MergePullRequestForm{ MergeMessageField: pr.Issue.Title, Do: string(repo_model.MergeStyleMerge), @@ -93,7 +94,7 @@ func TestAPICreatePullSuccess(t *testing.T) { owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) session := loginUser(t, owner11.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", owner10.Name, repo10.Name, token), &api.CreatePullRequestOption{ Head: fmt.Sprintf("%s:master", owner11.Name), Base: "master", @@ -113,7 +114,7 @@ func TestAPICreatePullWithFieldsSuccess(t *testing.T) { owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) session := loginUser(t, owner11.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) opts := &api.CreatePullRequestOption{ Head: fmt.Sprintf("%s:master", owner11.Name), @@ -150,7 +151,7 @@ func TestAPICreatePullWithFieldsFailure(t *testing.T) { owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) session := loginUser(t, owner11.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) opts := &api.CreatePullRequestOption{ Head: fmt.Sprintf("%s:master", owner11.Name), @@ -180,7 +181,7 @@ func TestAPIEditPull(t *testing.T) { owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID}) session := loginUser(t, owner10.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", owner10.Name, repo10.Name, token), &api.CreatePullRequestOption{ Head: "develop", Base: "master", diff --git a/tests/integration/api_releases_test.go b/tests/integration/api_releases_test.go index 12d2a02fb13d..d7f2a1b8b1b4 100644 --- a/tests/integration/api_releases_test.go +++ b/tests/integration/api_releases_test.go @@ -9,6 +9,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -24,7 +25,7 @@ func TestAPIListReleases(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - token := getUserToken(t, user2.LowerName) + token := getUserToken(t, user2.LowerName, auth_model.AccessTokenScopeRepo) link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/releases", user2.Name, repo.Name)) link.RawQuery = url.Values{"token": {token}}.Encode() @@ -100,7 +101,7 @@ func TestAPICreateAndUpdateRelease(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) gitRepo, err := git.OpenRepository(git.DefaultContext, repo.RepoPath()) assert.NoError(t, err) @@ -152,7 +153,7 @@ func TestAPICreateReleaseToDefaultBranch(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test") } @@ -163,7 +164,7 @@ func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) gitRepo, err := git.OpenRepository(git.DefaultContext, repo.RepoPath()) assert.NoError(t, err) @@ -213,7 +214,7 @@ func TestAPIDeleteReleaseByTagName(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") diff --git a/tests/integration/api_repo_archive_test.go b/tests/integration/api_repo_archive_test.go index a3c03ba2fcbc..fbcc12ccb62c 100644 --- a/tests/integration/api_repo_archive_test.go +++ b/tests/integration/api_repo_archive_test.go @@ -10,6 +10,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -24,7 +25,7 @@ func TestAPIDownloadArchive(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user2.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.zip", user2.Name, repo.Name)) link.RawQuery = url.Values{"token": {token}}.Encode() diff --git a/tests/integration/api_repo_collaborator_test.go b/tests/integration/api_repo_collaborator_test.go index 318c86e2c31f..ed015384772b 100644 --- a/tests/integration/api_repo_collaborator_test.go +++ b/tests/integration/api_repo_collaborator_test.go @@ -8,6 +8,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -27,7 +28,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { user10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10}) user11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 11}) - testCtx := NewAPITestContext(t, repo2Owner.Name, repo2.Name) + testCtx := NewAPITestContext(t, repo2Owner.Name, repo2.Name, auth_model.AccessTokenScopeRepo) t.Run("RepoOwnerShouldBeOwner", func(t *testing.T) { req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, repo2Owner.Name, testCtx.Token) @@ -84,7 +85,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) _session := loginUser(t, user5.Name) - _testCtx := NewAPITestContext(t, user5.Name, repo2.Name) + _testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, user5.Name, _testCtx.Token) resp := _session.MakeRequest(t, req, http.StatusOK) @@ -99,7 +100,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) _session := loginUser(t, user5.Name) - _testCtx := NewAPITestContext(t, user5.Name, repo2.Name) + _testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, user5.Name, _testCtx.Token) resp := _session.MakeRequest(t, req, http.StatusOK) @@ -115,7 +116,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user11.Name, perm.AccessModeRead)) _session := loginUser(t, user10.Name) - _testCtx := NewAPITestContext(t, user10.Name, repo2.Name) + _testCtx := NewAPITestContext(t, user10.Name, repo2.Name, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, user11.Name, _testCtx.Token) resp := _session.MakeRequest(t, req, http.StatusOK) diff --git a/tests/integration/api_repo_edit_test.go b/tests/integration/api_repo_edit_test.go index 716cebeb7cf4..9594b86d7ef5 100644 --- a/tests/integration/api_repo_edit_test.go +++ b/tests/integration/api_repo_edit_test.go @@ -9,6 +9,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" @@ -146,10 +147,10 @@ func TestAPIRepoEdit(t *testing.T) { // Get user2's token session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session) + token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Get user4's token session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) + token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Test editing a repo1 which user2 owns, changing name and many properties origRepoEditOption := getRepoEditOptionFromRepo(repo1) diff --git a/tests/integration/api_repo_file_create_test.go b/tests/integration/api_repo_file_create_test.go index 476441dbb136..b2098fdd0329 100644 --- a/tests/integration/api_repo_file_create_test.go +++ b/tests/integration/api_repo_file_create_test.go @@ -13,6 +13,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -150,10 +151,10 @@ func TestAPICreateFile(t *testing.T) { // Get user2's token session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session) + token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Get user4's token session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) + token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Test creating a file in repo1 which user2 owns, try both with branch and empty branch for _, branch := range [...]string{ @@ -279,7 +280,7 @@ func TestAPICreateFile(t *testing.T) { MakeRequest(t, req, http.StatusForbidden) // Test creating a file in an empty repository - doAPICreateRepository(NewAPITestContext(t, "user2", "empty-repo"), true)(t) + doAPICreateRepository(NewAPITestContext(t, "user2", "empty-repo", auth_model.AccessTokenScopeRepo), true)(t) createFileOptions = getCreateFileOptions() fileID++ treePath = fmt.Sprintf("new/file%d.txt", fileID) diff --git a/tests/integration/api_repo_file_delete_test.go b/tests/integration/api_repo_file_delete_test.go index 196d3208f5aa..9b80dc150a66 100644 --- a/tests/integration/api_repo_file_delete_test.go +++ b/tests/integration/api_repo_file_delete_test.go @@ -9,6 +9,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -48,10 +49,10 @@ func TestAPIDeleteFile(t *testing.T) { // Get user2's token session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session) + token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Get user4's token session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) + token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Test deleting a file in repo1 which user2 owns, try both with branch and empty branch for _, branch := range [...]string{ diff --git a/tests/integration/api_repo_file_get_test.go b/tests/integration/api_repo_file_get_test.go index 4fca55c93df8..a6a1e634396e 100644 --- a/tests/integration/api_repo_file_get_test.go +++ b/tests/integration/api_repo_file_get_test.go @@ -8,6 +8,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -24,7 +25,7 @@ func TestAPIGetRawFileOrLFS(t *testing.T) { // Test with LFS onGiteaRun(t, func(t *testing.T, u *url.URL) { - httpContext := NewAPITestContext(t, "user2", "repo-lfs-test") + httpContext := NewAPITestContext(t, "user2", "repo-lfs-test", auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeDeleteRepo) doAPICreateRepository(httpContext, false, func(t *testing.T, repository api.Repository) { u.Path = httpContext.GitPath() dstPath := t.TempDir() diff --git a/tests/integration/api_repo_file_update_test.go b/tests/integration/api_repo_file_update_test.go index 6dd06b712590..8e07511aafda 100644 --- a/tests/integration/api_repo_file_update_test.go +++ b/tests/integration/api_repo_file_update_test.go @@ -12,6 +12,7 @@ import ( "path/filepath" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -116,10 +117,10 @@ func TestAPIUpdateFile(t *testing.T) { // Get user2's token session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session) + token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Get user4's token session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) + token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Test updating a file in repo1 which user2 owns, try both with branch and empty branch for _, branch := range [...]string{ diff --git a/tests/integration/api_repo_git_hook_test.go b/tests/integration/api_repo_git_hook_test.go index a3bbe9bbad14..e1c4682e6d6e 100644 --- a/tests/integration/api_repo_git_hook_test.go +++ b/tests/integration/api_repo_git_hook_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -30,7 +31,7 @@ func TestAPIListGitHooks(t *testing.T) { // user1 is an admin user session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", owner.Name, repo.Name, token) resp := MakeRequest(t, req, http.StatusOK) @@ -56,7 +57,7 @@ func TestAPIListGitHooksNoHooks(t *testing.T) { // user1 is an admin user session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", owner.Name, repo.Name, token) resp := MakeRequest(t, req, http.StatusOK) @@ -76,7 +77,7 @@ func TestAPIListGitHooksNoAccess(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", owner.Name, repo.Name, token) MakeRequest(t, req, http.StatusForbidden) @@ -90,7 +91,7 @@ func TestAPIGetGitHook(t *testing.T) { // user1 is an admin user session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", owner.Name, repo.Name, token) resp := MakeRequest(t, req, http.StatusOK) @@ -107,7 +108,7 @@ func TestAPIGetGitHookNoAccess(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", owner.Name, repo.Name, token) MakeRequest(t, req, http.StatusForbidden) @@ -121,7 +122,7 @@ func TestAPIEditGitHook(t *testing.T) { // user1 is an admin user session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminRepoHook) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", owner.Name, repo.Name, token) @@ -150,7 +151,7 @@ func TestAPIEditGitHookNoAccess(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepoHook) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", owner.Name, repo.Name, token) req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditGitHookOption{ @@ -167,7 +168,7 @@ func TestAPIDeleteGitHook(t *testing.T) { // user1 is an admin user session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminRepoHook) req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", owner.Name, repo.Name, token) @@ -189,7 +190,7 @@ func TestAPIDeleteGitHookNoAccess(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepoHook) req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", owner.Name, repo.Name, token) MakeRequest(t, req, http.StatusForbidden) diff --git a/tests/integration/api_repo_git_tags_test.go b/tests/integration/api_repo_git_tags_test.go index 146b4b74bd7d..b29fc45cf553 100644 --- a/tests/integration/api_repo_git_tags_test.go +++ b/tests/integration/api_repo_git_tags_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -69,7 +70,7 @@ func TestAPIDeleteTagByName(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags/delete-tag?token=%s", owner.Name, repo.Name, token) diff --git a/tests/integration/api_repo_hook_test.go b/tests/integration/api_repo_hook_test.go index cf080575da6f..0fa2402992fc 100644 --- a/tests/integration/api_repo_hook_test.go +++ b/tests/integration/api_repo_hook_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -25,7 +26,7 @@ func TestAPICreateHook(t *testing.T) { // user1 is an admin user session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepoHook) completeURL := func(lastSegment string) string { return fmt.Sprintf("/api/v1/repos/%s/%s/%s?token=%s", owner.Name, repo.Name, lastSegment, token) } diff --git a/tests/integration/api_repo_lfs_migrate_test.go b/tests/integration/api_repo_lfs_migrate_test.go index 50d0c5966bad..e66ca6b1474b 100644 --- a/tests/integration/api_repo_lfs_migrate_test.go +++ b/tests/integration/api_repo_lfs_migrate_test.go @@ -8,6 +8,7 @@ import ( "path" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/lfs" @@ -30,7 +31,7 @@ func TestAPIRepoLFSMigrateLocal(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{ CloneAddr: path.Join(setting.RepoRootPath, "migration/lfs-test.git"), diff --git a/tests/integration/api_repo_lfs_test.go b/tests/integration/api_repo_lfs_test.go index c0ceaa8ba888..a7a70baeefa2 100644 --- a/tests/integration/api_repo_lfs_test.go +++ b/tests/integration/api_repo_lfs_test.go @@ -11,6 +11,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" @@ -59,7 +60,7 @@ func TestAPILFSMediaType(t *testing.T) { } func createLFSTestRepository(t *testing.T, name string) *repo_model.Repository { - ctx := NewAPITestContext(t, "user2", "lfs-"+name+"-repo") + ctx := NewAPITestContext(t, "user2", "lfs-"+name+"-repo", auth_model.AccessTokenScopeRepo) t.Run("CreateRepo", doAPICreateRepository(ctx, false)) repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "lfs-"+name+"-repo") diff --git a/tests/integration/api_repo_raw_test.go b/tests/integration/api_repo_raw_test.go index a35f1285b968..60e9eeed6bc1 100644 --- a/tests/integration/api_repo_raw_test.go +++ b/tests/integration/api_repo_raw_test.go @@ -7,6 +7,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/tests" @@ -19,7 +20,7 @@ func TestAPIReposRaw(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // Login as User2. session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) for _, ref := range [...]string{ "master", // Branch diff --git a/tests/integration/api_repo_tags_test.go b/tests/integration/api_repo_tags_test.go index 6c7ab7971c76..d4fd9097ddf3 100644 --- a/tests/integration/api_repo_tags_test.go +++ b/tests/integration/api_repo_tags_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" @@ -22,7 +23,7 @@ func TestAPIRepoTags(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // Login as User2. session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) repoName := "repo1" diff --git a/tests/integration/api_repo_teams_test.go b/tests/integration/api_repo_teams_test.go index 102f170d94f7..1f444e31414c 100644 --- a/tests/integration/api_repo_teams_test.go +++ b/tests/integration/api_repo_teams_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" @@ -27,7 +28,7 @@ func TestAPIRepoTeams(t *testing.T) { // user4 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // ListTeams url := fmt.Sprintf("/api/v1/repos/%s/teams?token=%s", publicOrgRepo.FullName(), token) @@ -67,7 +68,7 @@ func TestAPIRepoTeams(t *testing.T) { // AddTeam with user2 user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session = loginUser(t, user.Name) - token = getTokenForLoggedInUser(t, session) + token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) url = fmt.Sprintf("/api/v1/repos/%s/teams/%s?token=%s", publicOrgRepo.FullName(), "team1", token) req = NewRequest(t, "PUT", url) MakeRequest(t, req, http.StatusNoContent) diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go index 76850fb82746..76ceb779e00e 100644 --- a/tests/integration/api_repo_test.go +++ b/tests/integration/api_repo_test.go @@ -10,6 +10,7 @@ import ( "testing" "code.gitea.io/gitea/models" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" @@ -286,24 +287,17 @@ func TestAPIOrgRepos(t *testing.T) { count int includesPrivate bool }{ - nil: {count: 1}, + user: {count: 1}, user: {count: 3, includesPrivate: true}, user2: {count: 3, includesPrivate: true}, user3: {count: 1}, } for userToLogin, expected := range expectedResults { - var session *TestSession - var testName string - var token string - if userToLogin != nil && userToLogin.ID > 0 { - testName = fmt.Sprintf("LoggedUser%d", userToLogin.ID) - session = loginUser(t, userToLogin.Name) - token = getTokenForLoggedInUser(t, session) - } else { - testName = "AnonymousUser" - session = emptyTestSession(t) - } + testName := fmt.Sprintf("LoggedUser%d", userToLogin.ID) + session := loginUser(t, userToLogin.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) + t.Run(testName, func(t *testing.T) { req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token="+token, sourceOrg.Name) resp := MakeRequest(t, req, http.StatusOK) @@ -324,7 +318,7 @@ func TestAPIGetRepoByIDUnauthorized(t *testing.T) { defer tests.PrepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repositories/2?token="+token) MakeRequest(t, req, http.StatusNotFound) } @@ -348,7 +342,7 @@ func TestAPIRepoMigrate(t *testing.T) { for _, testCase := range testCases { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{ CloneAddr: testCase.cloneURL, RepoOwnerID: testCase.userID, @@ -378,7 +372,7 @@ func TestAPIRepoMigrateConflict(t *testing.T) { func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) { username := "user2" - baseAPITestContext := NewAPITestContext(t, username, "repo1") + baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeRepo) u.Path = baseAPITestContext.GitPath() @@ -413,7 +407,7 @@ func TestAPIMirrorSyncNonMirrorRepo(t *testing.T) { defer tests.PrepareTestEnv(t)() session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) var repo api.Repository req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1") @@ -445,7 +439,7 @@ func TestAPIOrgRepoCreate(t *testing.T) { for _, testCase := range testCases { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminOrg) req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos?token="+token, testCase.orgName), &api.CreateRepoOption{ Name: testCase.repoName, }) @@ -459,7 +453,7 @@ func TestAPIRepoCreateConflict(t *testing.T) { func testAPIRepoCreateConflict(t *testing.T, u *url.URL) { username := "user2" - baseAPITestContext := NewAPITestContext(t, username, "repo1") + baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeRepo) u.Path = baseAPITestContext.GitPath() @@ -509,7 +503,7 @@ func TestAPIRepoTransfer(t *testing.T) { // create repo to move user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) repoName := "moveME" apiRepo := new(api.Repository) req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{ @@ -527,7 +521,7 @@ func TestAPIRepoTransfer(t *testing.T) { user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID}) session = loginUser(t, user.Name) - token = getTokenForLoggedInUser(t, session) + token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer?token=%s", repo.OwnerName, repo.Name, token), &api.TransferRepoOption{ NewOwner: testCase.newOwner, TeamIDs: testCase.teams, @@ -544,7 +538,7 @@ func transfer(t *testing.T) *repo_model.Repository { // create repo to move user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) repoName := "moveME" apiRepo := new(api.Repository) req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{ @@ -574,7 +568,7 @@ func TestAPIAcceptTransfer(t *testing.T) { // try to accept with not authorized user session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token)) MakeRequest(t, req, http.StatusForbidden) @@ -584,7 +578,7 @@ func TestAPIAcceptTransfer(t *testing.T) { // accept transfer session = loginUser(t, "user4") - token = getTokenForLoggedInUser(t, session) + token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/accept?token=%s", repo.OwnerName, repo.Name, token)) resp := MakeRequest(t, req, http.StatusAccepted) @@ -600,7 +594,7 @@ func TestAPIRejectTransfer(t *testing.T) { // try to reject with not authorized user session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token)) MakeRequest(t, req, http.StatusForbidden) @@ -610,7 +604,7 @@ func TestAPIRejectTransfer(t *testing.T) { // reject transfer session = loginUser(t, "user4") - token = getTokenForLoggedInUser(t, session) + token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token)) resp := MakeRequest(t, req, http.StatusOK) @@ -624,7 +618,7 @@ func TestAPIGenerateRepo(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) templateRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 44}) @@ -660,7 +654,7 @@ func TestAPIRepoGetReviewers(t *testing.T) { defer tests.PrepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/reviewers?token=%s", user.Name, repo.Name, token) @@ -674,7 +668,7 @@ func TestAPIRepoGetAssignees(t *testing.T) { defer tests.PrepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/assignees?token=%s", user.Name, repo.Name, token) diff --git a/tests/integration/api_repo_topic_test.go b/tests/integration/api_repo_topic_test.go index 81eb1a9427df..ab9fd9bb96c5 100644 --- a/tests/integration/api_repo_topic_test.go +++ b/tests/integration/api_repo_topic_test.go @@ -9,6 +9,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -59,7 +60,7 @@ func TestAPIRepoTopic(t *testing.T) { repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // Get user2's token - token2 := getUserToken(t, user2.Name) + token2 := getUserToken(t, user2.Name, auth_model.AccessTokenScopeRepo) // Test read topics using login url := fmt.Sprintf("/api/v1/repos/%s/%s/topics", user2.Name, repo2.Name) @@ -139,7 +140,7 @@ func TestAPIRepoTopic(t *testing.T) { MakeRequest(t, req, http.StatusNotFound) // Get user4's token - token4 := getUserToken(t, user4.Name) + token4 := getUserToken(t, user4.Name, auth_model.AccessTokenScopeRepo) // Test read topics with write access url = fmt.Sprintf("/api/v1/repos/%s/%s/topics?token=%s", user3.Name, repo3.Name, token4) diff --git a/tests/integration/api_team_test.go b/tests/integration/api_team_test.go index 06d47bf70b05..27fe5e12e661 100644 --- a/tests/integration/api_team_test.go +++ b/tests/integration/api_team_test.go @@ -9,6 +9,7 @@ import ( "sort" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" @@ -29,7 +30,7 @@ func TestAPITeam(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser.UID}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminOrg) req := NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamUser.TeamID) resp := MakeRequest(t, req, http.StatusOK) @@ -43,7 +44,7 @@ func TestAPITeam(t *testing.T) { user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser2.UID}) session = loginUser(t, user2.Name) - token = getTokenForLoggedInUser(t, session) + token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamUser.TeamID) _ = MakeRequest(t, req, http.StatusForbidden) @@ -53,7 +54,7 @@ func TestAPITeam(t *testing.T) { // Get an admin user able to create, update and delete teams. user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) session = loginUser(t, user.Name) - token = getTokenForLoggedInUser(t, session) + token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminOrg) org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 6}) @@ -227,7 +228,7 @@ func TestAPITeamSearch(t *testing.T) { var results TeamSearchResults - token := getUserToken(t, user.Name) + token := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadOrg) req := NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "_team", token) resp := MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &results) @@ -237,7 +238,7 @@ func TestAPITeamSearch(t *testing.T) { // no access if not organization member user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) - token5 := getUserToken(t, user5.Name) + token5 := getUserToken(t, user5.Name, auth_model.AccessTokenScopeReadOrg) req = NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "team", token5) MakeRequest(t, req, http.StatusForbidden) @@ -252,7 +253,7 @@ func TestAPIGetTeamRepo(t *testing.T) { var results api.Repository - token := getUserToken(t, user.Name) + token := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadOrg) req := NewRequestf(t, "GET", "/api/v1/teams/%d/repos/%s/?token=%s", team.ID, teamRepo.FullName(), token) resp := MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &results) @@ -260,7 +261,7 @@ func TestAPIGetTeamRepo(t *testing.T) { // no access if not organization member user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) - token5 := getUserToken(t, user5.Name) + token5 := getUserToken(t, user5.Name, auth_model.AccessTokenScopeReadOrg) req = NewRequestf(t, "GET", "/api/v1/teams/%d/repos/%s/?token=%s", team.ID, teamRepo.FullName(), token5) MakeRequest(t, req, http.StatusNotFound) diff --git a/tests/integration/api_team_user_test.go b/tests/integration/api_team_user_test.go index a5078aedccb8..ec977fa572ed 100644 --- a/tests/integration/api_team_user_test.go +++ b/tests/integration/api_team_user_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" @@ -22,7 +23,7 @@ func TestAPITeamUser(t *testing.T) { normalUsername := "user2" session := loginUser(t, normalUsername) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) req := NewRequest(t, "GET", "/api/v1/teams/1/members/user1?token="+token) MakeRequest(t, req, http.StatusNotFound) diff --git a/tests/integration/api_user_email_test.go b/tests/integration/api_user_email_test.go index 147f703e9a41..09083d9ce8cd 100644 --- a/tests/integration/api_user_email_test.go +++ b/tests/integration/api_user_email_test.go @@ -7,6 +7,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -18,7 +19,7 @@ func TestAPIListEmails(t *testing.T) { normalUsername := "user2" session := loginUser(t, normalUsername) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) req := NewRequest(t, "GET", "/api/v1/user/emails?token="+token) resp := MakeRequest(t, req, http.StatusOK) @@ -45,7 +46,7 @@ func TestAPIAddEmail(t *testing.T) { normalUsername := "user2" session := loginUser(t, normalUsername) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeUser) opts := api.CreateEmailOption{ Emails: []string{"user101@example.com"}, @@ -82,7 +83,7 @@ func TestAPIDeleteEmail(t *testing.T) { normalUsername := "user2" session := loginUser(t, normalUsername) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeUser) opts := api.DeleteEmailOption{ Emails: []string{"user2-3@example.com"}, diff --git a/tests/integration/api_user_follow_test.go b/tests/integration/api_user_follow_test.go index 65749521cc63..c7ad62e64948 100644 --- a/tests/integration/api_user_follow_test.go +++ b/tests/integration/api_user_follow_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -24,7 +25,7 @@ func TestAPIFollow(t *testing.T) { token1 := getTokenForLoggedInUser(t, session1) session2 := loginUser(t, user2) - token2 := getTokenForLoggedInUser(t, session2) + token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeUserFollow) t.Run("Follow", func(t *testing.T) { defer tests.PrintCurrentTest(t)() diff --git a/tests/integration/api_user_org_perm_test.go b/tests/integration/api_user_org_perm_test.go index 8df418494a2c..ac575b1f012d 100644 --- a/tests/integration/api_user_org_perm_test.go +++ b/tests/integration/api_user_org_perm_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -32,7 +33,7 @@ func sampleTest(t *testing.T, auoptc apiUserOrgPermTestCase) { defer tests.PrepareTestEnv(t)() session := loginUser(t, auoptc.LoginUser) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/%s/orgs/%s/permissions?token=%s", auoptc.User, auoptc.Organization, token)) resp := MakeRequest(t, req, http.StatusOK) @@ -125,7 +126,7 @@ func TestUnknowUser(t *testing.T) { defer tests.PrepareTestEnv(t)() session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/unknow/orgs/org25/permissions?token=%s", token)) resp := MakeRequest(t, req, http.StatusNotFound) @@ -139,7 +140,7 @@ func TestUnknowOrganization(t *testing.T) { defer tests.PrepareTestEnv(t)() session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/user1/orgs/unknow/permissions?token=%s", token)) resp := MakeRequest(t, req, http.StatusNotFound) diff --git a/tests/integration/api_user_orgs_test.go b/tests/integration/api_user_orgs_test.go index 1f9ee2ea6eef..831ca018b458 100644 --- a/tests/integration/api_user_orgs_test.go +++ b/tests/integration/api_user_orgs_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" @@ -61,15 +62,14 @@ func TestUserOrgs(t *testing.T) { orgs = getUserOrgs(t, unrelatedUsername, privateMemberUsername) assert.Len(t, orgs, 0) - // not authenticated call also should hide org membership - orgs = getUserOrgs(t, "", privateMemberUsername) - assert.Len(t, orgs, 0) + // not authenticated call should not be allowed + testUserOrgsUnauthenticated(t, privateMemberUsername) } func getUserOrgs(t *testing.T, userDoer, userCheck string) (orgs []*api.Organization) { token := "" if len(userDoer) != 0 { - token = getUserToken(t, userDoer) + token = getUserToken(t, userDoer, auth_model.AccessTokenScopeReadOrg) } urlStr := fmt.Sprintf("/api/v1/users/%s/orgs?token=%s", userCheck, token) req := NewRequest(t, "GET", urlStr) @@ -78,6 +78,12 @@ func getUserOrgs(t *testing.T, userDoer, userCheck string) (orgs []*api.Organiza return orgs } +func testUserOrgsUnauthenticated(t *testing.T, userCheck string) { + session := emptyTestSession(t) + req := NewRequestf(t, "GET", "/api/v1/users/%s/orgs", userCheck) + session.MakeRequest(t, req, http.StatusUnauthorized) +} + func TestMyOrgs(t *testing.T) { defer tests.PrepareTestEnv(t)() @@ -85,7 +91,7 @@ func TestMyOrgs(t *testing.T) { MakeRequest(t, req, http.StatusUnauthorized) normalUsername := "user2" - token := getUserToken(t, normalUsername) + token := getUserToken(t, normalUsername, auth_model.AccessTokenScopeReadOrg) req = NewRequest(t, "GET", "/api/v1/user/orgs?token="+token) resp := MakeRequest(t, req, http.StatusOK) var orgs []*api.Organization diff --git a/tests/integration/api_user_star_test.go b/tests/integration/api_user_star_test.go index 63363f22de16..6a486c19a882 100644 --- a/tests/integration/api_user_star_test.go +++ b/tests/integration/api_user_star_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -22,11 +23,12 @@ func TestAPIStar(t *testing.T) { session := loginUser(t, user) token := getTokenForLoggedInUser(t, session) + tokenWithRepoScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) t.Run("Star", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, token)) + req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithRepoScope)) MakeRequest(t, req, http.StatusNoContent) }) @@ -47,7 +49,7 @@ func TestAPIStar(t *testing.T) { t.Run("GetMyStarredRepos", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred?token=%s", token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred?token=%s", tokenWithRepoScope)) resp := MakeRequest(t, req, http.StatusOK) assert.Equal(t, "1", resp.Header().Get("X-Total-Count")) @@ -61,17 +63,17 @@ func TestAPIStar(t *testing.T) { t.Run("IsStarring", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithRepoScope)) MakeRequest(t, req, http.StatusNoContent) - req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo+"notexisting", token)) + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo+"notexisting", tokenWithRepoScope)) MakeRequest(t, req, http.StatusNotFound) }) t.Run("Unstar", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, token)) + req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithRepoScope)) MakeRequest(t, req, http.StatusNoContent) }) } diff --git a/tests/integration/api_user_watch_test.go b/tests/integration/api_user_watch_test.go index 295e639fd18c..570296257396 100644 --- a/tests/integration/api_user_watch_test.go +++ b/tests/integration/api_user_watch_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -22,11 +23,12 @@ func TestAPIWatch(t *testing.T) { session := loginUser(t, user) token := getTokenForLoggedInUser(t, session) + tokenWithRepoScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) t.Run("Watch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, token)) + req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, tokenWithRepoScope)) MakeRequest(t, req, http.StatusOK) }) @@ -47,7 +49,7 @@ func TestAPIWatch(t *testing.T) { t.Run("GetMyWatchedRepos", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/subscriptions?token=%s", token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/subscriptions?token=%s", tokenWithRepoScope)) resp := MakeRequest(t, req, http.StatusOK) assert.Equal(t, "1", resp.Header().Get("X-Total-Count")) @@ -61,17 +63,17 @@ func TestAPIWatch(t *testing.T) { t.Run("IsWatching", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, tokenWithRepoScope)) MakeRequest(t, req, http.StatusOK) - req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo+"notexisting", token)) + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo+"notexisting", tokenWithRepoScope)) MakeRequest(t, req, http.StatusNotFound) }) t.Run("Unwatch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, token)) + req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, tokenWithRepoScope)) MakeRequest(t, req, http.StatusNoContent) }) } diff --git a/tests/integration/api_wiki_test.go b/tests/integration/api_wiki_test.go index 546f4d0e3e8e..3f85074c8aa7 100644 --- a/tests/integration/api_wiki_test.go +++ b/tests/integration/api_wiki_test.go @@ -9,6 +9,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -179,7 +180,7 @@ func TestAPINewWikiPage(t *testing.T) { defer tests.PrepareTestEnv(t)() username := "user2" session := loginUser(t, username) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/new?token=%s", username, "repo1", token) @@ -196,7 +197,7 @@ func TestAPIEditWikiPage(t *testing.T) { defer tests.PrepareTestEnv(t)() username := "user2" session := loginUser(t, username) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/Page-With-Spaced-Name?token=%s", username, "repo1", token) diff --git a/tests/integration/dump_restore_test.go b/tests/integration/dump_restore_test.go index e34738aaf1eb..9ad795d53a41 100644 --- a/tests/integration/dump_restore_test.go +++ b/tests/integration/dump_restore_test.go @@ -14,6 +14,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -50,7 +51,7 @@ func TestDumpRestore(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // // Phase 1: dump repo1 from the Gitea instance to the filesystem diff --git a/tests/integration/eventsource_test.go b/tests/integration/eventsource_test.go index e810a9fa246b..4fdb8cd6f52d 100644 --- a/tests/integration/eventsource_test.go +++ b/tests/integration/eventsource_test.go @@ -10,6 +10,7 @@ import ( "time" activities_model "code.gitea.io/gitea/models/activities" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -59,7 +60,7 @@ func TestEventSourceManagerRun(t *testing.T) { thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) session := loginUser(t, user2.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeNotification) var apiNL []api.NotificationThread diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go index f7e1e04b1e97..a11bad21b7b0 100644 --- a/tests/integration/git_test.go +++ b/tests/integration/git_test.go @@ -16,6 +16,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/perm" @@ -42,11 +43,11 @@ func TestGit(t *testing.T) { func testGit(t *testing.T, u *url.URL) { username := "user2" - baseAPITestContext := NewAPITestContext(t, username, "repo1") + baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeWritePublicKey, auth_model.AccessTokenScopeDeleteRepo) u.Path = baseAPITestContext.GitPath() - forkedUserCtx := NewAPITestContext(t, "user4", "repo1") + forkedUserCtx := NewAPITestContext(t, "user4", "repo1", auth_model.AccessTokenScopeRepo) t.Run("HTTP", func(t *testing.T) { defer tests.PrintCurrentTest(t)() @@ -357,7 +358,7 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes t.Run("CreateBranchProtected", doGitCreateBranch(dstPath, "protected")) t.Run("PushProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected")) - ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame) + ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeRepo) t.Run("ProtectProtectedBranchNoWhitelist", doProtectBranch(ctx, "protected", "", "")) t.Run("GenerateCommit", func(t *testing.T) { _, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") @@ -601,7 +602,7 @@ func doAutoPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) { return func(t *testing.T) { defer tests.PrintCurrentTest(t)() - ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame) + ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeRepo) t.Run("CheckoutProtected", doGitCheckoutBranch(dstPath, "protected")) t.Run("PullProtected", doGitPull(dstPath, "origin", "protected")) diff --git a/tests/integration/gpg_git_test.go b/tests/integration/gpg_git_test.go index 669212ff14a1..36095694b0e8 100644 --- a/tests/integration/gpg_git_test.go +++ b/tests/integration/gpg_git_test.go @@ -10,6 +10,7 @@ import ( "os" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/process" @@ -69,7 +70,7 @@ func TestGPGGit(t *testing.T) { t.Run("Unsigned-Initial", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { assert.NotNil(t, branch.Commit) @@ -93,7 +94,7 @@ func TestGPGGit(t *testing.T) { t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile( t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) { assert.False(t, response.Verification.Verified) @@ -110,7 +111,7 @@ func TestGPGGit(t *testing.T) { t.Run("Unsigned-Initial-CRUD-Never", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) t.Run("CreateCRUDFile-Never", crudActionCreateFile( t, testCtx, user, "parentsigned", "parentsigned-never", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) { assert.False(t, response.Verification.Verified) @@ -123,7 +124,7 @@ func TestGPGGit(t *testing.T) { t.Run("Unsigned-Initial-CRUD-Always", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) t.Run("CreateCRUDFile-Always", crudActionCreateFile( t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) { assert.NotNil(t, response.Verification) @@ -160,7 +161,7 @@ func TestGPGGit(t *testing.T) { t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) t.Run("CreateCRUDFile-Always-ParentSigned", crudActionCreateFile( t, testCtx, user, "always", "always-parentsigned", "signed-always-parentsigned.txt", func(t *testing.T, response api.FileResponse) { assert.NotNil(t, response.Verification) @@ -183,7 +184,7 @@ func TestGPGGit(t *testing.T) { t.Run("AlwaysSign-Initial", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-always") + testCtx := NewAPITestContext(t, username, "initial-always", auth_model.AccessTokenScopeRepo) t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { assert.NotNil(t, branch.Commit) @@ -211,7 +212,7 @@ func TestGPGGit(t *testing.T) { t.Run("AlwaysSign-Initial-CRUD-Never", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-always-never") + testCtx := NewAPITestContext(t, username, "initial-always-never", auth_model.AccessTokenScopeRepo) t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) t.Run("CreateCRUDFile-Never", crudActionCreateFile( t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) { @@ -224,7 +225,7 @@ func TestGPGGit(t *testing.T) { u.Path = baseAPITestContext.GitPath() t.Run("AlwaysSign-Initial-CRUD-ParentSigned-On-Always", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-always-parent") + testCtx := NewAPITestContext(t, username, "initial-always-parent", auth_model.AccessTokenScopeRepo) t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile( t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) { @@ -243,7 +244,7 @@ func TestGPGGit(t *testing.T) { t.Run("AlwaysSign-Initial-CRUD-Always", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-always-always") + testCtx := NewAPITestContext(t, username, "initial-always-always", auth_model.AccessTokenScopeRepo) t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) t.Run("CreateCRUDFile-Always", crudActionCreateFile( t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) { @@ -263,7 +264,7 @@ func TestGPGGit(t *testing.T) { t.Run("UnsignedMerging", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) var err error t.Run("CreatePullRequest", func(t *testing.T) { pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "never2")(t) @@ -284,7 +285,7 @@ func TestGPGGit(t *testing.T) { t.Run("BaseSignedMerging", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) var err error t.Run("CreatePullRequest", func(t *testing.T) { pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "parentsigned2")(t) @@ -305,7 +306,7 @@ func TestGPGGit(t *testing.T) { t.Run("CommitsSignedMerging", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) var err error t.Run("CreatePullRequest", func(t *testing.T) { pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "always-parentsigned")(t) diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 911d9ddf4c77..fbb6322785af 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -21,6 +21,7 @@ import ( "testing" "time" + "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/json" @@ -217,8 +218,8 @@ func emptyTestSession(t testing.TB) *TestSession { return &TestSession{jar: jar} } -func getUserToken(t testing.TB, userName string) string { - return getTokenForLoggedInUser(t, loginUser(t, userName)) +func getUserToken(t testing.TB, userName string, scope ...auth.AccessTokenScope) string { + return getTokenForLoggedInUser(t, loginUser(t, userName), scope...) } func loginUser(t testing.TB, userName string) *TestSession { @@ -256,7 +257,10 @@ func loginUserWithPassword(t testing.TB, userName, password string) *TestSession // token has to be unique this counter take care of var tokenCounter int64 -func getTokenForLoggedInUser(t testing.TB, session *TestSession) string { +// getTokenForLoggedInUser returns a token for a logged in user. +// The scope is an optional list of snake_case strings like the frontend form fields, +// but without the "scope_" prefix. +func getTokenForLoggedInUser(t testing.TB, session *TestSession, scopes ...auth.AccessTokenScope) string { t.Helper() var token string req := NewRequest(t, "GET", "/user/settings/applications") @@ -274,10 +278,13 @@ func getTokenForLoggedInUser(t testing.TB, session *TestSession) string { csrf = doc.GetCSRF() } assert.NotEmpty(t, csrf) - req = NewRequestWithValues(t, "POST", "/user/settings/applications", map[string]string{ - "_csrf": csrf, - "name": fmt.Sprintf("api-testing-token-%d", atomic.AddInt64(&tokenCounter, 1)), - }) + urlValues := url.Values{} + urlValues.Add("_csrf", csrf) + urlValues.Add("name", fmt.Sprintf("api-testing-token-%d", atomic.AddInt64(&tokenCounter, 1))) + for _, scope := range scopes { + urlValues.Add("scope", string(scope)) + } + req = NewRequestWithURLValues(t, "POST", "/user/settings/applications", urlValues) resp = session.MakeRequest(t, req, http.StatusSeeOther) // Log the flash values on failure @@ -317,6 +324,11 @@ func NewRequestWithValues(t testing.TB, method, urlStr string, values map[string for key, value := range values { urlValues[key] = []string{value} } + return NewRequestWithURLValues(t, method, urlStr, urlValues) +} + +func NewRequestWithURLValues(t testing.TB, method, urlStr string, urlValues url.Values) *http.Request { + t.Helper() req := NewRequestWithBody(t, method, urlStr, bytes.NewBufferString(urlValues.Encode())) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") return req diff --git a/tests/integration/migrate_test.go b/tests/integration/migrate_test.go index 9eca69cfcf20..a925493d7c9e 100644 --- a/tests/integration/migrate_test.go +++ b/tests/integration/migrate_test.go @@ -11,6 +11,7 @@ import ( "path/filepath" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -66,7 +67,7 @@ func TestMigrateGiteaForm(t *testing.T) { repoName := "repo1" repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: ownerName}) session := loginUser(t, ownerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Step 0: verify the repo is available req := NewRequestf(t, "GET", fmt.Sprintf("/%s/%s", ownerName, repoName)) diff --git a/tests/integration/org_count_test.go b/tests/integration/org_count_test.go index a6fe7f188eb1..8f850a170f11 100644 --- a/tests/integration/org_count_test.go +++ b/tests/integration/org_count_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -24,7 +25,7 @@ func testOrgCounts(t *testing.T, u *url.URL) { orgOwner := "user2" orgName := "testOrg" orgCollaborator := "user4" - ctx := NewAPITestContext(t, orgOwner, "repo1") + ctx := NewAPITestContext(t, orgOwner, "repo1", auth_model.AccessTokenScopeAdminOrg) var ownerCountRepos map[string]int var collabCountRepos map[string]int diff --git a/tests/integration/org_test.go b/tests/integration/org_test.go index 09a5f42082cc..bfa6380e8a98 100644 --- a/tests/integration/org_test.go +++ b/tests/integration/org_test.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" @@ -158,7 +159,7 @@ func TestOrgRestrictedUser(t *testing.T) { // Therefore create a read-only team adminSession := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, adminSession) + token := getTokenForLoggedInUser(t, adminSession, auth_model.AccessTokenScopeAdminOrg) teamToCreate := &api.CreateTeamOption{ Name: "codereader", diff --git a/tests/integration/privateactivity_test.go b/tests/integration/privateactivity_test.go index 06019406d791..6e1377ae1f2f 100644 --- a/tests/integration/privateactivity_test.go +++ b/tests/integration/privateactivity_test.go @@ -9,6 +9,7 @@ import ( "testing" activities_model "code.gitea.io/gitea/models/activities" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -33,7 +34,7 @@ func testPrivateActivityDoSomethingForActionEntries(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) session := loginUser(t, privateActivityTestUser) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token) req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{ Body: "test", diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index e72d00ffb360..491fc0e0aafe 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -17,6 +17,7 @@ import ( "time" "code.gitea.io/gitea/models" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -217,7 +218,7 @@ func TestCantMergeConflict(t *testing.T) { testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n") // Use API to create a conflicting pr - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", "user1", "repo1", token), &api.CreatePullRequestOption{ Head: "conflict", Base: "base", @@ -325,7 +326,7 @@ func TestCantMergeUnrelated(t *testing.T) { testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n") // Use API to create a conflicting pr - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", "user1", "repo1", token), &api.CreatePullRequestOption{ Head: "unrelated", Base: "base", diff --git a/tests/integration/pull_status_test.go b/tests/integration/pull_status_test.go index bca8ec848b81..e60d17edc02c 100644 --- a/tests/integration/pull_status_test.go +++ b/tests/integration/pull_status_test.go @@ -11,6 +11,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" @@ -63,7 +64,7 @@ func TestPullCreate_CommitStatus(t *testing.T) { api.CommitStatusWarning: "gitea-exclamation", } - testCtx := NewAPITestContext(t, "user1", "repo1") + testCtx := NewAPITestContext(t, "user1", "repo1", auth_model.AccessTokenScopeRepo) // Update commit status, and check if icon is updated as well for _, status := range statusList { diff --git a/tests/integration/pull_update_test.go b/tests/integration/pull_update_test.go index 1e20a63e6628..bd416e5bcfac 100644 --- a/tests/integration/pull_update_test.go +++ b/tests/integration/pull_update_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" @@ -38,7 +39,7 @@ func TestAPIPullUpdate(t *testing.T) { assert.NoError(t, pr.LoadIssue(db.DefaultContext)) session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/pulls/%d/update?token="+token, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Issue.Index) session.MakeRequest(t, req, http.StatusOK) @@ -66,7 +67,7 @@ func TestAPIPullUpdateByRebase(t *testing.T) { assert.NoError(t, pr.LoadIssue(db.DefaultContext)) session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/pulls/%d/update?style=rebase&token="+token, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Issue.Index) session.MakeRequest(t, req, http.StatusOK) diff --git a/tests/integration/repo_commits_test.go b/tests/integration/repo_commits_test.go index ab90e72877e5..cbd83c6deb17 100644 --- a/tests/integration/repo_commits_test.go +++ b/tests/integration/repo_commits_test.go @@ -11,6 +11,7 @@ import ( "sync" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -50,7 +51,8 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) { assert.NotEmpty(t, commitURL) // Call API to add status for commit - t.Run("CreateStatus", doAPICreateCommitStatus(NewAPITestContext(t, "user2", "repo1"), path.Base(commitURL), api.CommitStatusState(state))) + ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeRepo) + t.Run("CreateStatus", doAPICreateCommitStatus(ctx, path.Base(commitURL), api.CommitStatusState(state))) req = NewRequest(t, "GET", "/user2/repo1/commits/branch/master") resp = session.MakeRequest(t, req, http.StatusOK) @@ -142,7 +144,8 @@ func TestRepoCommitsStatusParallel(t *testing.T) { wg.Add(1) go func(parentT *testing.T, i int) { parentT.Run(fmt.Sprintf("ParallelCreateStatus_%d", i), func(t *testing.T) { - runBody := doAPICreateCommitStatus(NewAPITestContext(t, "user2", "repo1"), path.Base(commitURL), api.CommitStatusState("pending")) + ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeRepoStatus) + runBody := doAPICreateCommitStatus(ctx, path.Base(commitURL), api.CommitStatusState("pending")) runBody(t) wg.Done() }) diff --git a/tests/integration/ssh_key_test.go b/tests/integration/ssh_key_test.go index 89a27743032c..1e9dc264a64c 100644 --- a/tests/integration/ssh_key_test.go +++ b/tests/integration/ssh_key_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" @@ -47,7 +48,9 @@ func TestPushDeployKeyOnEmptyRepo(t *testing.T) { func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) { // OK login - ctx := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1") + ctx := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1", auth_model.AccessTokenScopeRepo) + ctxWithDeleteRepo := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1", auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeDeleteRepo) + keyname := fmt.Sprintf("%s-push", ctx.Reponame) u.Path = ctx.GitPath() @@ -72,7 +75,7 @@ func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) { t.Run("CheckIsNotEmpty", doCheckRepositoryEmptyStatus(ctx, false)) - t.Run("DeleteRepository", doAPIDeleteRepository(ctx)) + t.Run("DeleteRepository", doAPIDeleteRepository(ctxWithDeleteRepo)) }) } @@ -89,10 +92,13 @@ func testKeyOnlyOneType(t *testing.T, u *url.URL) { keyname := fmt.Sprintf("%s-push", reponame) // OK login - ctx := NewAPITestContext(t, username, reponame) + ctx := NewAPITestContext(t, username, reponame, auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeAdminPublicKey) + ctxWithDeleteRepo := NewAPITestContext(t, username, reponame, auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeAdminPublicKey, auth_model.AccessTokenScopeDeleteRepo) otherCtx := ctx otherCtx.Reponame = "ssh-key-test-repo-2" + otherCtxWithDeleteRepo := ctxWithDeleteRepo + otherCtxWithDeleteRepo.Reponame = otherCtx.Reponame failCtx := ctx failCtx.ExpectedCode = http.StatusUnprocessableEntity @@ -160,7 +166,7 @@ func testKeyOnlyOneType(t *testing.T, u *url.URL) { otherSSHURL := createSSHUrl(otherCtx.GitPath(), u) dstOtherPath := t.TempDir() - t.Run("DeleteRepository", doAPIDeleteRepository(ctx)) + t.Run("DeleteRepository", doAPIDeleteRepository(ctxWithDeleteRepo)) t.Run("FailToCreateUserKeyAsStillDeploy", doAPICreateUserKey(failCtx, keyname, keyFile)) @@ -170,9 +176,9 @@ func testKeyOnlyOneType(t *testing.T, u *url.URL) { t.Run("PushToOther", doGitPushTestRepository(dstOtherPath, "origin", "master")) - t.Run("DeleteOtherRepository", doAPIDeleteRepository(otherCtx)) + t.Run("DeleteOtherRepository", doAPIDeleteRepository(otherCtxWithDeleteRepo)) - t.Run("RecreateRepository", doAPICreateRepository(ctx, false)) + t.Run("RecreateRepository", doAPICreateRepository(ctxWithDeleteRepo, false)) t.Run("CreateUserKey", doAPICreateUserKey(ctx, keyname, keyFile, func(t *testing.T, publicKey api.PublicKey) { userKeyPublicKeyID = publicKey.ID diff --git a/tests/integration/user_test.go b/tests/integration/user_test.go index eeaa6d6e00ae..febfe576cf9b 100644 --- a/tests/integration/user_test.go +++ b/tests/integration/user_test.go @@ -7,6 +7,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -165,7 +166,7 @@ Note: This user hasn't uploaded any GPG keys. // Import key // User1 session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteGPGKey) testCreateGPGKey(t, session.MakeRequest, token, http.StatusCreated, `-----BEGIN PGP PUBLIC KEY BLOCK----- mQENBFyy/VUBCADJ7zbM20Z1RWmFoVgp5WkQfI2rU1Vj9cQHes9i42wVLLtcbPeo diff --git a/web_src/js/markup/asciicast.js b/web_src/js/markup/asciicast.js new file mode 100644 index 000000000000..d77c05b7aa5f --- /dev/null +++ b/web_src/js/markup/asciicast.js @@ -0,0 +1,14 @@ +export async function renderAsciinemaPlayer() { + const els = document.querySelectorAll('.asciinema-player-container'); + if (!els.length) return; + + const player = await import(/* webpackChunkName: "asciinema-player" */'asciinema-player'); + + for (const el of els) { + player.create(el.getAttribute('data-asciinema-player-src'), el, { + // poster (a preview frame) to display until the playback is started. + // Set it to 1 hour (also means the end if the video is shorter) to make the preview frame show more. + poster: 'npt:1:0:0', + }); + } +} diff --git a/web_src/js/markup/content.js b/web_src/js/markup/content.js index 319c229385a3..e4ec3d0b4baa 100644 --- a/web_src/js/markup/content.js +++ b/web_src/js/markup/content.js @@ -1,6 +1,7 @@ import {renderMermaid} from './mermaid.js'; import {renderMath} from './math.js'; import {renderCodeCopy} from './codecopy.js'; +import {renderAsciinemaPlayer} from './asciicast.js'; import {initMarkupTasklist} from './tasklist.js'; // code that runs for all markup content @@ -8,6 +9,7 @@ export function initMarkupContent() { renderMermaid(); renderMath(); renderCodeCopy(); + renderAsciinemaPlayer(); } // code that only runs for comments diff --git a/web_src/less/_repository.less b/web_src/less/_repository.less index 646cf4e60e28..4bcaf8dd040f 100644 --- a/web_src/less/_repository.less +++ b/web_src/less/_repository.less @@ -470,6 +470,10 @@ pre { overflow: auto; } + + .asciicast { + padding: 5px !important; + } } .sidebar { diff --git a/web_src/less/index.less b/web_src/less/index.less index 2d670ac2d5d3..185bf7ca313b 100644 --- a/web_src/less/index.less +++ b/web_src/less/index.less @@ -13,6 +13,7 @@ @import "./markup/content.less"; @import "./markup/codecopy.less"; @import "./code/linebutton.less"; +@import "./markup/asciicast.less"; @import "./chroma/base.less"; @import "./chroma/light.less"; diff --git a/web_src/less/markup/asciicast.less b/web_src/less/markup/asciicast.less new file mode 100644 index 000000000000..468f0b4f3fab --- /dev/null +++ b/web_src/less/markup/asciicast.less @@ -0,0 +1,10 @@ +@import "../asciinema-player/dist/bundle/asciinema-player.css"; + +.asciinema-player-container { + width: 100%; + height: auto; +} + +.asciinema-terminal { + overflow: hidden !important; +}