Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CR-15777 #666

Merged
merged 8 commits into from
Jan 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION=v0.1.26
VERSION=v0.1.27

OUT_DIR=dist
YEAR?=$(shell date +"%Y")
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.19

require (
github.com/Masterminds/semver/v3 v3.1.1
github.com/argoproj-labs/argocd-autopilot v0.4.9
github.com/argoproj-labs/argocd-autopilot v0.4.10
github.com/argoproj/argo-cd/v2 v2.5.2
github.com/argoproj/argo-events v0.17.1-0.20220327045437-70eaafe9afec
github.com/argoproj/argo-workflows/v3 v3.3.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ github.com/antonmedv/expr v1.9.0/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmH
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/appscode/go v0.0.0-20190808133642-1d4ef1f1c1e0/go.mod h1:iy07dV61Z7QQdCKJCIvUoDL21u6AIceRhZzyleh2ymc=
github.com/argoproj-labs/argocd-autopilot v0.4.9 h1:mXptimJTxZhpgq2lRZTQLO4OHyGyNZpsIxYPdWmdnOY=
github.com/argoproj-labs/argocd-autopilot v0.4.9/go.mod h1:iR9JosRv7UBOQBSDR64Rrg1qs1DURhR+kx8zK2GEap4=
github.com/argoproj-labs/argocd-autopilot v0.4.10 h1:0I0U28wvwoZkh2/ECKEgMDFw71Dzh6WCSAU4TkWuxY8=
github.com/argoproj-labs/argocd-autopilot v0.4.10/go.mod h1:iR9JosRv7UBOQBSDR64Rrg1qs1DURhR+kx8zK2GEap4=
github.com/argoproj/argo-cd/v2 v2.5.2 h1:hyPi8NFXW3tG2yURslIMI20GfCdTN1/BDnt4+v5lpoA=
github.com/argoproj/argo-cd/v2 v2.5.2/go.mod h1:3ToENm286PFVlZKNMutBzOwNyhevz4fw9dcgyiq3FIY=
github.com/argoproj/argo-events v0.17.1-0.20220327045437-70eaafe9afec h1:95S2LPUUdPO2jYxuR5z1uk1GL2m/u+ud2iFAr5gK6VI=
Expand Down
47 changes: 47 additions & 0 deletions pkg/git/provider_gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ package git

import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"path"
"strings"

apgit "github.com/argoproj-labs/argocd-autopilot/pkg/git"
httputil "github.com/codefresh-io/cli-v2/pkg/util/http"
Expand All @@ -32,6 +35,11 @@ type (
apiURL *url.URL
c *http.Client
}

gitlabUserResponse struct {
Username string `json:"username"`
Bot bool `json:"bot"`
}
)

const (
Expand Down Expand Up @@ -81,6 +89,16 @@ func (g *gitlab) VerifyUserToken(ctx context.Context, auth apgit.Auth) error {
// if it returns 400 - the token has "api" scope
// otherwise - the token does not have the scope
func (g *gitlab) checkApiScope(ctx context.Context, token string) error {

tokenType, err := g.checkTokenType(token, ctx)
if err != nil {
return fmt.Errorf("failed checking api scope: %w", err)
}

if tokenType == "project" {
return errors.New("runtime git-token is invalid, project token is not exceptable")
}

res, err := g.request(ctx, token, http.MethodPost, "projects")
if err != nil {
return fmt.Errorf("failed checking api scope: %w", err)
Expand All @@ -94,6 +112,35 @@ func (g *gitlab) checkApiScope(ctx context.Context, token string) error {
return nil
}

func (g *gitlab) checkTokenType(token string, ctx context.Context) (string, error) {
userRes, err := g.request(ctx, token, http.MethodGet, "user")

if err != nil {
return "", fmt.Errorf("failed getting user: %w", err)
}

defer userRes.Body.Close()

bodyBytes, err := io.ReadAll(userRes.Body)
if err != nil {
return "", fmt.Errorf("failed reading user body: %w", err)
}

var user gitlabUserResponse
err = json.Unmarshal(bodyBytes, &user)
if err != nil {
return "", fmt.Errorf("failed parse user body: %w", err)
}
if user.Bot {
if strings.HasPrefix(user.Username, "project") {
return "project", nil
}
return "group", nil
}

return "personal", nil
}

// HEAD to projects.
// if it returns 200 - the token has "repo_read" scope
// otherwise - the token does not have the scope
Expand Down
53 changes: 52 additions & 1 deletion pkg/git/provider_gitlab_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ package git

import (
"context"
"encoding/json"
"errors"
"io"
"net/http"
"strings"
"testing"

"github.com/codefresh-io/cli-v2/pkg/git/mocks"
Expand All @@ -38,22 +41,70 @@ func Test_gitlab_checkApiScope(t *testing.T) {
wantErr string
beforeFn func(t *testing.T, c *mocks.MockRoundTripper)
}{
"Should fail if POST fails": {
"Should fail if POST projects fails": {
wantErr: "failed checking api scope: Post \"https://some.server/api/v4/projects\": some error",
beforeFn: func(_ *testing.T, c *mocks.MockRoundTripper) {
c.EXPECT().RoundTrip(gomock.AssignableToTypeOf(&http.Request{})).Times(1).DoAndReturn(func(req *http.Request) (*http.Response, error) {
assert.Equal(t, "GET", req.Method)
assert.Equal(t, "https://some.server/api/v4/user", req.URL.String())
body, _ := json.Marshal(&gitlabUserResponse{
Username: "username",
Bot: false,
})
bodyReader := io.NopCloser(strings.NewReader(string(body[:])))
res := &http.Response{
StatusCode: 200,
Body: bodyReader,
}
return res, nil
})
c.EXPECT().RoundTrip(gomock.AssignableToTypeOf(&http.Request{})).Return(nil, errors.New("some error"))
},
},
"Should fail if GET user fails": {
wantErr: "failed checking api scope: failed getting user: Get \"https://some.server/api/v4/user\": some error",
beforeFn: func(_ *testing.T, c *mocks.MockRoundTripper) {
c.EXPECT().RoundTrip(gomock.AssignableToTypeOf(&http.Request{})).Return(nil, errors.New("some error"))
},
},
"Should fail if POST fails with 403": {
wantErr: "git-token is invalid or missing required \"api\" scope",
beforeFn: func(_ *testing.T, c *mocks.MockRoundTripper) {
c.EXPECT().RoundTrip(gomock.AssignableToTypeOf(&http.Request{})).Times(1).DoAndReturn(func(req *http.Request) (*http.Response, error) {
assert.Equal(t, "GET", req.Method)
assert.Equal(t, "https://some.server/api/v4/user", req.URL.String())
body, _ := json.Marshal(&gitlabUserResponse{
Username: "username",
Bot: false,
})
bodyReader := io.NopCloser(strings.NewReader(string(body[:])))
res := &http.Response{
StatusCode: 200,
Body: bodyReader,
}
return res, nil
})
c.EXPECT().RoundTrip(gomock.AssignableToTypeOf(&http.Request{})).Times(1).Return(&http.Response{
StatusCode: http.StatusForbidden,
}, nil)
},
},
"Should succeed if POST returns 400": {
beforeFn: func(t *testing.T, c *mocks.MockRoundTripper) {
c.EXPECT().RoundTrip(gomock.AssignableToTypeOf(&http.Request{})).Times(1).DoAndReturn(func(req *http.Request) (*http.Response, error) {
assert.Equal(t, "GET", req.Method)
assert.Equal(t, "https://some.server/api/v4/user", req.URL.String())
body, _ := json.Marshal(&gitlabUserResponse{
Username: "username",
Bot: false,
})
bodyReader := io.NopCloser(strings.NewReader(string(body[:])))
res := &http.Response{
StatusCode: 200,
Body: bodyReader,
}
return res, nil
})
c.EXPECT().RoundTrip(gomock.AssignableToTypeOf(&http.Request{})).Times(1).DoAndReturn(func(req *http.Request) (*http.Response, error) {
assert.Equal(t, "POST", req.Method)
assert.Equal(t, "https://some.server/api/v4/projects", req.URL.String())
Expand Down