diff --git a/server/forge/bitbucket/bitbucket.go b/server/forge/bitbucket/bitbucket.go index 735141fe2..eff097798 100644 --- a/server/forge/bitbucket/bitbucket.go +++ b/server/forge/bitbucket/bitbucket.go @@ -398,8 +398,23 @@ func (c *config) PullRequests(ctx context.Context, u *model.User, r *model.Repo, // Hook parses the incoming Bitbucket hook and returns the Repository and // Pipeline details. If the hook is unsupported nil values are returned. -func (c *config) Hook(_ context.Context, req *http.Request) (*model.Repo, *model.Pipeline, error) { - return parseHook(req) +func (c *config) Hook(ctx context.Context, req *http.Request) (*model.Repo, *model.Pipeline, error) { + repo, pl, err := parseHook(req) + if err != nil { + return nil, nil, err + } + + u, err := common.RepoUserForgeID(ctx, repo.ForgeRemoteID) + if err != nil { + return nil, nil, err + } + + repo, err = c.Repo(ctx, u, repo.ForgeRemoteID, repo.Owner, repo.Name) + if err != nil { + return nil, nil, err + } + + return repo, pl, nil } // OrgMembership returns if user is member of organization and if user diff --git a/server/forge/bitbucket/bitbucket_test.go b/server/forge/bitbucket/bitbucket_test.go index c6479d47f..99db30659 100644 --- a/server/forge/bitbucket/bitbucket_test.go +++ b/server/forge/bitbucket/bitbucket_test.go @@ -23,11 +23,14 @@ import ( "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "go.woodpecker-ci.org/woodpecker/v3/server/forge/bitbucket/fixtures" "go.woodpecker-ci.org/woodpecker/v3/server/forge/bitbucket/internal" "go.woodpecker-ci.org/woodpecker/v3/server/forge/types" "go.woodpecker-ci.org/woodpecker/v3/server/model" + "go.woodpecker-ci.org/woodpecker/v3/server/store" + mocks_store "go.woodpecker-ci.org/woodpecker/v3/server/store/mocks" ) func TestNew(t *testing.T) { @@ -203,9 +206,15 @@ func TestBitbucket(t *testing.T) { req.Header = http.Header{} req.Header.Set(hookEvent, hookPush) + mockStore := mocks_store.NewStore(t) + ctx = store.InjectToContext(ctx, mockStore) + mockStore.On("GetUser", mock.Anything).Return(fakeUser, nil) + mockStore.On("GetRepoForgeID", mock.Anything).Return(fakeRepoFromHook, nil) + r, b, err := c.Hook(ctx, req) assert.NoError(t, err) assert.Equal(t, "martinherren1984/publictestrepo", r.FullName) + assert.Equal(t, "master", r.Branch) assert.Equal(t, "c14c1bb05dfb1fdcdf06b31485fff61b0ea44277", b.Commit) } @@ -269,6 +278,13 @@ var ( FullName: "test_name/hook_empty", } + fakeRepoFromHook = &model.Repo{ + Owner: "martinherren1984", + Name: "publictestrepo", + FullName: "martinherren1984/publictestrepo", + UserID: 1, + } + fakePipeline = &model.Pipeline{ Commit: "9ecad50", } diff --git a/server/forge/bitbucket/fixtures/handler.go b/server/forge/bitbucket/fixtures/handler.go index 146dcfef8..938924035 100644 --- a/server/forge/bitbucket/fixtures/handler.go +++ b/server/forge/bitbucket/fixtures/handler.go @@ -90,6 +90,8 @@ func getRepo(c *gin.Context) { c.String(http.StatusNotFound, "") case "permission_read", "permission_write", "permission_admin": c.String(http.StatusOK, fmt.Sprintf(permissionRepoPayload, c.Param("name"))) + case "{898477b2-a080-4089-b385-597a783db392}": + c.String(http.StatusOK, repoPayloadFromHook) default: c.String(http.StatusOK, repoPayload) } @@ -227,6 +229,137 @@ const permissionRepoPayload = ` } ` +const repoPayloadFromHook = ` +{ + "type": "repository", + "full_name": "martinherren1984/publictestrepo", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/martinherren1984/publictestrepo" + }, + "html": { + "href": "https://bitbucket.org/martinherren1984/publictestrepo" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7B898477b2-a080-4089-b385-597a783db392%7D?ts=default" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/martinherren1984/publictestrepo/pullrequests" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/martinherren1984/publictestrepo/commits" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/martinherren1984/publictestrepo/forks" + }, + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/martinherren1984/publictestrepo/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/martinherren1984/publictestrepo/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/martinherren1984/publictestrepo/refs/tags" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/martinherren1984/publictestrepo/downloads" + }, + "source": { + "href": "https://api.bitbucket.org/2.0/repositories/martinherren1984/publictestrepo/src" + }, + "clone": [ + { + "name": "https", + "href": "https://bitbucket.org/martinherren1984/publictestrepo.git" + }, + { + "name": "ssh", + "href": "git@bitbucket.org:martinherren1984/publictestrepo.git" + } + ], + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/martinherren1984/publictestrepo/hooks" + } + }, + "name": "PublicTestRepo", + "slug": "publictestrepo", + "description": "", + "scm": "git", + "website": null, + "owner": { + "display_name": "Martin Herren", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7Bc5a0d676-fd27-4bd4-ac69-a7540d7b495b%7D" + }, + "avatar": { + "href": "https://secure.gravatar.com/avatar/37de364488b2ec474b5458ca86442bbb?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FMH-2.png" + }, + "html": { + "href": "https://bitbucket.org/%7Bc5a0d676-fd27-4bd4-ac69-a7540d7b495b%7D/" + } + }, + "type": "user", + "uuid": "{c5a0d676-fd27-4bd4-ac69-a7540d7b495b}", + "account_id": "5cf8e3a9678ca90f8e7cc8a8", + "nickname": "Martin Herren" + }, + "workspace": { + "type": "workspace", + "uuid": "{c5a0d676-fd27-4bd4-ac69-a7540d7b495b}", + "name": "Martin Herren", + "slug": "martinherren1984", + "links": { + "avatar": { + "href": "https://bitbucket.org/workspaces/martinherren1984/avatar/?ts=1658761964" + }, + "html": { + "href": "https://bitbucket.org/martinherren1984/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/martinherren1984" + } + } + }, + "is_private": false, + "project": { + "type": "project", + "key": "PUB", + "uuid": "{2cede481-f59e-49ec-88d0-a85629b7925d}", + "name": "PublicTestProject", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/martinherren1984/projects/PUB" + }, + "html": { + "href": "https://bitbucket.org/martinherren1984/workspace/projects/PUB" + }, + "avatar": { + "href": "https://bitbucket.org/martinherren1984/workspace/projects/PUB/avatar/32?ts=1658768453" + } + } + }, + "fork_policy": "allow_forks", + "created_on": "2022-07-25T17:01:20.950706+00:00", + "updated_on": "2022-09-07T20:19:30.622886+00:00", + "size": 85955, + "language": "", + "uuid": "{898477b2-a080-4089-b385-597a783db392}", + "mainbranch": { + "name": "master", + "type": "branch" + }, + "override_settings": { + "default_merge_strategy": true, + "branching_model": true + }, + "parent": null, + "enforced_signed_commits": null, + "has_issues": false, + "has_wiki": false +} +` + const repoHookPayload = ` { "pagelen": 10, diff --git a/server/forge/common/utils.go b/server/forge/common/utils.go index 549e048cc..3026aa822 100644 --- a/server/forge/common/utils.go +++ b/server/forge/common/utils.go @@ -16,6 +16,7 @@ package common import ( "context" + "errors" "net" "net/url" "strings" @@ -49,18 +50,38 @@ func UserToken(ctx context.Context, r *model.Repo, u *model.User) string { return u.AccessToken } - _store, ok := store.TryFromContext(ctx) - if !ok { - log.Error().Msg("could not get store from context") - return "" - } - if r == nil { - log.Error().Msg("cannot get user token by empty repo") - return "" - } - user, err := _store.GetUser(r.UserID) + user, err := RepoUser(ctx, r) if err != nil { + log.Error().Err(err).Msg("could not get repo user") return "" } return user.AccessToken } + +func RepoUser(ctx context.Context, r *model.Repo) (*model.User, error) { + _store, ok := store.TryFromContext(ctx) + if !ok { + return nil, errors.New("could not get store from context") + } + if r == nil { + log.Error().Msg("cannot get user token by empty repo") + return nil, errors.New("cannot get user token by empty repo") + } + user, err := _store.GetUser(r.UserID) + if err != nil { + return nil, err + } + return user, nil +} + +func RepoUserForgeID(ctx context.Context, repoForgeID model.ForgeRemoteID) (*model.User, error) { + _store, ok := store.TryFromContext(ctx) + if !ok { + return nil, errors.New("could not get store from context") + } + r, err := _store.GetRepoForgeID(repoForgeID) + if err != nil { + return nil, err + } + return RepoUser(ctx, r) +}