Skip to content

Commit

Permalink
Merge pull request #250 from somtochiama/add-user-login
Browse files Browse the repository at this point in the history
Validate user ref when creating user repository
  • Loading branch information
souleb authored Oct 12, 2023
2 parents 8772525 + c8137f4 commit 9789fae
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 18 deletions.
24 changes: 24 additions & 0 deletions gitea/client_repositories_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,19 @@ func (c *UserRepositoriesClient) listUserRepos(username string) ([]*gitea.Reposi
return validateRepositoryObjects(apiObjs)
}

// GetUserLogin returns the authenticated user
func (c *UserRepositoriesClient) GetUserLogin(ctx context.Context) (gitprovider.IdentityRef, error) {
// GET /user
user, _, err := c.c.GetMyUserInfo()
if err != nil {
return nil, err
}
return gitprovider.UserRef{
Domain: c.domain,
UserLogin: user.UserName,
}, nil
}

// Create creates a repository for the given organization, with the data and options
//
// ErrAlreadyExists will be returned if the resource already exists.
Expand All @@ -108,6 +121,17 @@ func (c *UserRepositoriesClient) Create(ctx context.Context,
return nil, err
}

// extra validation to ensure we don't create a project when the wrong owner
// is passed in
idRef, err := c.GetUserLogin(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get owner from API")
}

if ref.GetIdentity() != idRef.GetIdentity() {
return nil, gitprovider.NewErrIncorrectUser(ref.GetIdentity())
}

apiObj, err := createRepository(ctx, c.c, ref, "", req, opts...)
if err != nil {
return nil, err
Expand Down
17 changes: 11 additions & 6 deletions gitea/integration_repositories_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ var _ = Describe("Gitea Provider", func() {
Expect(*info.DefaultBranch).To(Equal(defaultBranch))
}

It("should get the current user", func() {
user, err := c.UserRepositories().GetUserLogin(ctx)
Expect(err).ToNot(HaveOccurred())

Expect(user.GetIdentity()).To(Equal(giteaUser))
})

It("should be possible to create a user repository", func() {
// First, check what repositories are available
repos, err := c.UserRepositories().List(ctx, newUserRef(giteaUser))
Expand Down Expand Up @@ -100,17 +107,15 @@ var _ = Describe("Gitea Provider", func() {
Expect(getSpec.Equals(postSpec)).To(BeTrue())
})

It("should return correct repo info when creating a repository with wrong UserLogin", func() {
It("should fail when creating a repository with wrong UserLogin", func() {
repoName := fmt.Sprintf("test-user-repo-creation-%03d", rand.Intn(1000))
repoRef := newUserRepoRef(repoName)
repoRef.UserLogin = "yadda-yadda-yada"

repo, err := c.UserRepositories().Create(ctx, repoRef, gitprovider.RepositoryInfo{})
_, err := c.UserRepositories().Create(ctx, repoRef, gitprovider.RepositoryInfo{})

Expect(err).To(BeNil())
Expect(
repo.Repository().GetCloneURL(gitprovider.TransportTypeHTTPS)).
To(Equal(fmt.Sprintf("%s/%s/%s.git", giteaBaseUrl, giteaUser, repoName)))
expectedErr := gitprovider.NewErrIncorrectUser(repoRef.UserLogin)
Expect(err).To(MatchError(expectedErr))
})

It("should error at creation time if the repo already does exist", func() {
Expand Down
24 changes: 24 additions & 0 deletions github/client_repositories_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ type UserRepositoriesClient struct {
*clientContext
}

// GetUserLogin returns the current authenticated user.
func (c *UserRepositoriesClient) GetUserLogin(ctx context.Context) (gitprovider.IdentityRef, error) {
// GET /user
user, err := c.c.GetUser(ctx)
if err != nil {
return nil, err
}
return gitprovider.UserRef{
Domain: c.domain,
UserLogin: user.GetLogin(),
}, nil
}

// Get returns the repository at the given path.
//
// ErrNotFound is returned if the resource does not exist.
Expand Down Expand Up @@ -88,6 +101,17 @@ func (c *UserRepositoriesClient) Create(ctx context.Context,
return nil, err
}

// extra validation to ensure we don't create a project when the wrong owner
// is passed in
idRef, err := c.GetUserLogin(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get owner from API")
}

if ref.GetIdentity() != idRef.GetIdentity() {
return nil, gitprovider.NewErrIncorrectUser(ref.GetIdentity())
}

apiObj, err := createRepository(ctx, c.c, ref, "", req, opts...)
if err != nil {
return nil, err
Expand Down
9 changes: 9 additions & 0 deletions github/githubclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ type githubClient interface {
// DANGEROUS COMMAND: In order to use this, you must set destructiveActions to true.
DeleteRepo(ctx context.Context, owner, repo string) error

// GetUser is a wrapper for "GET /user"
GetUser(ctx context.Context) (*github.User, error)

// ListKeys is a wrapper for "GET /repos/{owner}/{repo}/keys".
// This function handles pagination, HTTP error wrapping, and validates the server result.
ListKeys(ctx context.Context, owner, repo string) ([]*github.Key, error)
Expand Down Expand Up @@ -297,6 +300,12 @@ func (c *githubClientImpl) ListKeys(ctx context.Context, owner, repo string) ([]
return apiObjs, nil
}

func (c *githubClientImpl) GetUser(ctx context.Context) (*github.User, error) {
// GET /user
user, _, err := c.c.Users.Get(ctx, "")
return user, err
}

func (c *githubClientImpl) ListCommitsPage(ctx context.Context, owner, repo, branch string, perPage int, page int) ([]*github.Commit, error) {
apiObjs := make([]*github.Commit, 0)
lcOpts := &github.CommitsListOptions{
Expand Down
17 changes: 11 additions & 6 deletions github/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,13 @@ var _ = Describe("GitHub Provider", func() {
}
}

It("should get the current user", func() {
user, err := c.UserRepositories().GetUserLogin(ctx)
Expect(err).ToNot(HaveOccurred())

Expect(user.GetIdentity()).To(Equal(testUser))
})

It("should list the available organizations the user has access to", func() {
// Get a list of all organizations the user is part of
orgs, err := c.Organizations().List(ctx)
Expand Down Expand Up @@ -345,16 +352,14 @@ var _ = Describe("GitHub Provider", func() {
Expect(getSpec.Equals(postSpec)).To(BeTrue())
})

It("should return correct repo info when creating a repository with wrong UserLogin", func() {
It("should fail when creating a repository with wrong UserLogin", func() {
repoName := fmt.Sprintf("test-user-repo-creation-%03d", rand.Intn(1000))
repoRef := newUserRepoRef("yadda-yadda-yada", repoName)

repo, err := c.UserRepositories().Create(ctx, repoRef, gitprovider.RepositoryInfo{})
_, err := c.UserRepositories().Create(ctx, repoRef, gitprovider.RepositoryInfo{})

Expect(err).To(BeNil())
Expect(
repo.Repository().GetCloneURL(gitprovider.TransportTypeHTTPS)).
To(Equal(fmt.Sprintf("https://%s/%s/%s.git", githubDomain, testUser, repoName)))
expectedErr := gitprovider.NewErrIncorrectUser(repoRef.GetIdentity())
Expect(err).To(MatchError(expectedErr))
})

It("should error at creation time if the repo already does exist", func() {
Expand Down
24 changes: 24 additions & 0 deletions gitlab/client_repositories_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ type UserRepositoriesClient struct {
*clientContext
}

// GetUserLogin returns the current authenticated user.
func (c *UserRepositoriesClient) GetUserLogin(ctx context.Context) (gitprovider.IdentityRef, error) {
// GET /user
user, err := c.c.GetUser(ctx)
if err != nil {
return nil, err
}
return &gitprovider.UserRef{
Domain: c.domain,
UserLogin: user.Username,
}, nil
}

// Get returns the repository at the given path.
//
// ErrNotFound is returned if the resource does not exist.
Expand Down Expand Up @@ -89,6 +102,17 @@ func (c *UserRepositoriesClient) Create(ctx context.Context,
return nil, err
}

// extra validation to ensure we don't create a project when the wrong owner
// is passed in
idRef, err := c.GetUserLogin(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get owner from API")
}

if ref.GetIdentity() != idRef.GetIdentity() {
return nil, gitprovider.NewErrIncorrectUser(ref.GetIdentity())
}

apiObj, err := createProject(ctx, c.c, ref, "", req, opts...)
if err != nil {
return nil, err
Expand Down
9 changes: 9 additions & 0 deletions gitlab/gitlabclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ type gitlabClient interface {
// DANGEROUS COMMAND: In order to use this, you must set destructiveActions to true.
DeleteProject(ctx context.Context, projectName string) error

// GetUser is a wrapper for "GET /user"
GetUser(ctx context.Context) (*gitlab.User, error)

// Deploy key methods

// ListKeys is a wrapper for "GET /projects/{project}/deploy_keys".
Expand Down Expand Up @@ -341,6 +344,12 @@ func (c *gitlabClientImpl) DeleteProject(ctx context.Context, projectName string
return err
}

func (c *gitlabClientImpl) GetUser(ctx context.Context) (*gitlab.User, error) {
// GET /user
proj, _, err := c.c.Users.CurrentUser(gitlab.WithContext(ctx))
return proj, err
}

func (c *gitlabClientImpl) ListKeys(projectName string) ([]*gitlab.ProjectDeployKey, error) {
apiObjs := []*gitlab.ProjectDeployKey{}
opts := &gitlab.ListProjectDeployKeysOptions{}
Expand Down
17 changes: 11 additions & 6 deletions gitlab/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,13 @@ var _ = Describe("GitLab Provider", func() {
}
}

It("should get the current user", func() {
user, err := c.UserRepositories().GetUserLogin(ctx)
Expect(err).ToNot(HaveOccurred())

Expect(user.GetIdentity()).To(Equal(testUserName))
})

It("should list the available organizations the user has access to", func() {
// Get a list of all organizations the user is part of
orgs, err := c.Organizations().List(ctx)
Expand Down Expand Up @@ -796,16 +803,14 @@ var _ = Describe("GitLab Provider", func() {
Expect(errors.Is(err, gitprovider.ErrAlreadyExists)).To(BeTrue())
})

It("should return correct repo info when creating a repository with wrong UserLogin", func() {
It("should fail info when creating a repository with wrong UserLogin", func() {
repoName := fmt.Sprintf("test-user-repo-creation-%03d", rand.Intn(1000))
repoRef := newUserRepoRef(testBaseUrl, "yadda-yadda-yada", repoName)

repo, err := c.UserRepositories().Create(ctx, repoRef, gitprovider.RepositoryInfo{})
_, err := c.UserRepositories().Create(ctx, repoRef, gitprovider.RepositoryInfo{})

Expect(err).To(BeNil())
Expect(
repo.Repository().GetCloneURL(gitprovider.TransportTypeHTTPS)).
To(Equal(fmt.Sprintf("%s/%s/%s.git", testBaseUrl, testUserName, repoName)))
expectedErr := gitprovider.NewErrIncorrectUser(repoRef.GetIdentity())
Expect(err).To(MatchError(expectedErr))
})

It("should update if the user repo already exists when reconciling", func() {
Expand Down
3 changes: 3 additions & 0 deletions gitprovider/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ type UserRepositoriesClient interface {
// ErrAlreadyExists will be returned if the resource already exists.
Create(ctx context.Context, r UserRepositoryRef, req RepositoryInfo, opts ...RepositoryCreateOption) (UserRepository, error)

// GetUserLogin returns the current authenticated user.
GetUserLogin(ctx context.Context) (IdentityRef, error)

// Reconcile makes sure the given desired state (req) becomes the actual state in the backing Git provider.
//
// If req doesn't exist under the hood, it is created (actionTaken == true).
Expand Down
18 changes: 18 additions & 0 deletions gitprovider/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package gitprovider

import (
"errors"
"fmt"
"net/http"
"time"
)
Expand Down Expand Up @@ -131,3 +132,20 @@ type InvalidCredentialsError struct {
// InvalidCredentialsError extends HTTPError.
HTTPError `json:",inline"`
}

// ErrIncorrectUser describes that the user provided was incorrect
//
// It is returned by `UserRepositories().Create` when an incorrect UserLogin is passed in
type ErrIncorrectUser struct {
user string
}

// Error implements the error interface.
func NewErrIncorrectUser(user string) *ErrIncorrectUser {
return &ErrIncorrectUser{user}
}

// Error implements the error interface.
func (e *ErrIncorrectUser) Error() string {
return fmt.Sprintf("incorrect user '%s' provided", e.user)
}
9 changes: 9 additions & 0 deletions stash/client_repositories_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ type UserRepositoriesClient struct {
*clientContext
}

// GetUserLogin returns the authenticated user.
//
// Stash currently doesn't have an endpoint for this, so this
// is mostly to implement the interface.
func (c *UserRepositoriesClient) GetUserLogin(ctx context.Context) (gitprovider.IdentityRef, error) {
// TODO: call API for stash
return gitprovider.UserRef{}, nil
}

// Get returns the repository at the given path.
// ErrNotFound is returned if the resource does not exist.
func (c *UserRepositoriesClient) Get(ctx context.Context, ref gitprovider.UserRepositoryRef) (gitprovider.UserRepository, error) {
Expand Down

0 comments on commit 9789fae

Please sign in to comment.