Skip to content

Commit 894a0ce

Browse files
CR-8938 verify github token's scopes (#232)
* wip * verify github token's scopes * removed unnesecery packages * added index for different providers inferring provider from installation repo * wip * small fix * small fix * bug fix * support gitlab * improve errors and logs * small addition * small fixes * bump * after review * small fix * modify decorator * wip * wip * bump * wip * wip * codegen * improvements * wip * bump * removeed stringifyArray * removed log * bump
1 parent cf6178a commit 894a0ce

File tree

7 files changed

+138
-15
lines changed

7 files changed

+138
-15
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
VERSION=v0.0.219
1+
VERSION=v0.0.220
22

33
OUT_DIR=dist
44
YEAR?=$(shell date +"%Y")

cmd/commands/common.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import (
3232
"github.com/codefresh-io/cli-v2/pkg/log"
3333
"github.com/codefresh-io/cli-v2/pkg/store"
3434
"github.com/codefresh-io/cli-v2/pkg/util"
35+
cfgit "github.com/codefresh-io/cli-v2/pkg/git"
36+
3537
"github.com/manifoldco/promptui"
3638
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3739
"k8s.io/client-go/tools/clientcmd"
@@ -247,10 +249,32 @@ func getIngressClassFromUserSelect(ctx context.Context, ingressClassNames []stri
247249
return nil
248250
}
249251

252+
func inferProviderFromRepo(opts *git.CloneOptions) {
253+
if opts.Provider != "" {
254+
return
255+
}
256+
257+
if strings.Contains(opts.Repo, "github.com") {
258+
opts.Provider = "github"
259+
}
260+
if strings.Contains(opts.Repo, "gitlab.com") {
261+
opts.Provider = "gitlab"
262+
}
263+
}
264+
250265
func ensureGitToken(cmd *cobra.Command, cloneOpts *git.CloneOptions) error {
251266
if cloneOpts.Auth.Password == "" && !store.Get().Silent {
252-
return getGitTokenFromUserInput(cmd)
267+
err := getGitTokenFromUserInput(cmd)
268+
if err != nil {
269+
return err
270+
}
253271
}
272+
273+
err := cfgit.VerifyToken(cmd.Context(), cloneOpts.Provider, cloneOpts.Auth.Password)
274+
if err != nil {
275+
return fmt.Errorf("failed to verify git token: %w", err)
276+
}
277+
254278
return nil
255279
}
256280

cmd/commands/runtime.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ func NewRuntimeInstallCommand() *cobra.Command {
187187
err := runtimeInstallCommandPreRunHandler(cmd, &installationOpts)
188188
handleCliStep(reporter.InstallPhasePreCheckFinish, "Finished pre installation checks", err, false)
189189
if err != nil {
190-
return fmt.Errorf("Pre installation error: %w", err)
190+
return util.DecorateErrorWithDocsLink(fmt.Errorf("Pre installation error: %w", err), store.Get().RequirementsLink)
191191
}
192192

193193
finalParameters = map[string]string{
@@ -272,7 +272,7 @@ func runtimeInstallCommandPreRunHandler(cmd *cobra.Command, opts *RuntimeInstall
272272
err = ensureIngressClass(cmd.Context(), opts)
273273
handleCliStep(reporter.InstallStepPreCheckEnsureIngressClass, "Getting ingress class", err, false)
274274
if err != nil {
275-
return err
275+
return util.DecorateErrorWithDocsLink(err, store.Get().RequirementsLink)
276276
}
277277

278278
err = ensureIngressHost(cmd, opts)
@@ -287,6 +287,8 @@ func runtimeInstallCommandPreRunHandler(cmd *cobra.Command, opts *RuntimeInstall
287287
return err
288288
}
289289

290+
inferProviderFromRepo(opts.InsCloneOpts)
291+
290292
err = ensureGitToken(cmd, opts.InsCloneOpts)
291293
handleCliStep(reporter.InstallStepPreCheckEnsureGitToken, "Getting git token", err, false)
292294
if err != nil {
@@ -422,7 +424,7 @@ func ensureIngressClass(ctx context.Context, opts *RuntimeInstallOptions) error
422424
cs := opts.KubeFactory.KubernetesClientSetOrDie()
423425
ingressClassList, err := cs.NetworkingV1().IngressClasses().List(ctx, metav1.ListOptions{})
424426
if err != nil {
425-
return fmt.Errorf("failed to get ingressClass list from your cluster: %w", err)
427+
return fmt.Errorf("failed to get ingress class list from your cluster: %w", err)
426428
}
427429

428430
var ingressClassNames []string
@@ -443,11 +445,11 @@ func ensureIngressClass(ctx context.Context, opts *RuntimeInstallOptions) error
443445
opts.IngressController = ingressClassNameToController[opts.IngressClass]
444446
return nil
445447
}
446-
return fmt.Errorf("Ingress Class '%s' is not supported. Only the ingress class of type NGINX is supported. for more information: %s", opts.IngressClass, store.Get().RequirementsLink)
448+
return fmt.Errorf("ingress class '%s' is not supported. only the ingress class of type nginx is supported.", opts.IngressClass)
447449
}
448450

449451
if len(ingressClassNames) == 0 {
450-
return fmt.Errorf("No ingressClasses of type nginx were found. please install a nginx ingress controller on your cluster before installing a runtime. see instructions at: %s", store.Get().RequirementsLink)
452+
return fmt.Errorf("no ingress classes of type nginx were found. please install a nginx ingress controller on your cluster before installing a runtime.")
451453
}
452454

453455
if len(ingressClassNames) == 1 {
@@ -467,7 +469,7 @@ func ensureIngressClass(ctx context.Context, opts *RuntimeInstallOptions) error
467469
return nil
468470
}
469471

470-
return fmt.Errorf("Please add the --ingress-class flag and define its value")
472+
return fmt.Errorf("please add the --ingress-class flag and define its value")
471473
}
472474

473475
func getComponents(rt *runtime.Runtime, opts *RuntimeInstallOptions) []string {
@@ -767,11 +769,11 @@ func preInstallationChecks(ctx context.Context, opts *RuntimeInstallOptions) err
767769
}
768770

769771
if rt.Spec.DefVersion.GreaterThan(store.Get().MaxDefVersion) {
770-
err = fmt.Errorf("your cli version is out of date. please upgrade to the latest version before installing. for more information: %s", store.Get().DownloadCliLink)
772+
err = fmt.Errorf("your cli version is out of date. please upgrade to the latest version before installing.")
771773
}
772774
handleCliStep(reporter.InstallStepRunPreCheckEnsureCliVersion, "Checking CLI version", err, false)
773775
if err != nil {
774-
return err
776+
return util.DecorateErrorWithDocsLink(err, store.Get().DownloadCliLink)
775777
}
776778

777779
err = checkRuntimeCollisions(ctx, opts.RuntimeName, opts.KubeFactory)
@@ -1699,10 +1701,13 @@ func createCodefreshArgoDashboardAgent(ctx context.Context, path string, cloneOp
16991701

17001702
func ensureGitIntegrationOpts(opts *RuntimeInstallOptions) error {
17011703
var err error
1704+
17021705
if opts.InsCloneOpts.Provider == "" {
17031706
if opts.GitIntegrationCreationOpts.Provider, err = inferProviderFromCloneURL(opts.InsCloneOpts.URL()); err != nil {
17041707
return err
17051708
}
1709+
} else {
1710+
opts.GitIntegrationCreationOpts.Provider = apmodel.GitProviders(strings.ToUpper(opts.InsCloneOpts.Provider))
17061711
}
17071712

17081713
if opts.GitIntegrationCreationOpts.APIURL == "" {

docs/releases/release_notes.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ cf version
2323

2424
```bash
2525
# download and extract the binary
26-
curl -L --output - https://github.com/codefresh-io/cli-v2/releases/download/v0.0.219/cf-linux-amd64.tar.gz | tar zx
26+
curl -L --output - https://github.com/codefresh-io/cli-v2/releases/download/v0.0.220/cf-linux-amd64.tar.gz | tar zx
2727

2828
# move the binary to your $PATH
2929
mv ./cf-linux-amd64 /usr/local/bin/cf
@@ -36,7 +36,7 @@ cf version
3636

3737
```bash
3838
# download and extract the binary
39-
curl -L --output - https://github.com/codefresh-io/cli-v2/releases/download/v0.0.219/cf-darwin-amd64.tar.gz | tar zx
39+
curl -L --output - https://github.com/codefresh-io/cli-v2/releases/download/v0.0.220/cf-darwin-amd64.tar.gz | tar zx
4040

4141
# move the binary to your $PATH
4242
mv ./cf-darwin-amd64 /usr/local/bin/cf

manifests/runtime.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ metadata:
55
namespace: '{{ namespace }}'
66
spec:
77
defVersion: 1.0.0
8-
version: 0.0.219
8+
version: 0.0.220
99
bootstrapSpecifier: github.com/codefresh-io/cli-v2/manifests/argo-cd
1010
components:
1111
- name: events

pkg/git/git.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright 2022 The Codefresh Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package git
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"net/http"
21+
"strings"
22+
23+
"github.com/codefresh-io/cli-v2/pkg/log"
24+
)
25+
26+
27+
var (
28+
requiredGitHubScopes = []string{ "repo", "admin:repo_hook" }
29+
)
30+
31+
32+
func VerifyToken(ctx context.Context, provider string, token string) error {
33+
providerToVerifier := map[string]func(context.Context, string)error {
34+
"github": verifyGitHubTokenScope,
35+
}
36+
37+
verifier := providerToVerifier[provider]
38+
if verifier == nil {
39+
return verifyTokenScopeFallback(ctx, provider)
40+
}
41+
42+
return verifier(ctx, token)
43+
}
44+
45+
func verifyGitHubTokenScope(ctx context.Context, token string) error {
46+
errMessage := "the provided git token is missing one or more of the required scopes:" + strings.Join(requiredGitHubScopes, ", ")
47+
48+
req, err := http.NewRequestWithContext(ctx, "HEAD", "https://api.github.com/", nil)
49+
if err != nil {
50+
return err
51+
}
52+
53+
req.Header.Set("Authorization", fmt.Sprintf("token %s", token))
54+
55+
resp, err := http.DefaultClient.Do(req)
56+
if err != nil {
57+
return err
58+
}
59+
60+
defer resp.Body.Close()
61+
62+
rawScopes := resp.Header["X-Oauth-Scopes"]
63+
var scopes []string
64+
if len(rawScopes) > 0 {
65+
scopes = strings.Split(rawScopes[0], ", ")
66+
}
67+
68+
for _, rs := range requiredGitHubScopes {
69+
var contained bool
70+
for _, scope := range scopes {
71+
if scope == rs {
72+
contained = true
73+
break
74+
}
75+
}
76+
77+
if !contained {
78+
return fmt.Errorf(errMessage)
79+
}
80+
}
81+
82+
return nil
83+
}
84+
85+
func verifyTokenScopeFallback(ctx context.Context, token string) error {
86+
log.G(ctx).Info("Skipping token verification")
87+
88+
return nil
89+
}

pkg/util/util.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,12 @@ func CurrentServer() (string, error) {
171171
return server, nil
172172
}
173173

174-
func DecorateErrorWithDocsLink(err error) error {
175-
return fmt.Errorf("%s\nfor more information: %s", err.Error(), store.Get().DocsLink)
174+
func DecorateErrorWithDocsLink(err error, link ...string) error {
175+
if len(link) == 0 {
176+
return fmt.Errorf("%s\nfor more information: %s", err.Error(), store.Get().DocsLink)
177+
}
178+
179+
return fmt.Errorf("%s\nfor more information: %s", err.Error(), link[0])
176180
}
177181

178182
func reportCancel(status reporter.CliStepStatus) {
@@ -183,3 +187,4 @@ func reportCancel(status reporter.CliStepStatus) {
183187
Err: nil,
184188
})
185189
}
190+

0 commit comments

Comments
 (0)