浏览代码

Merge pull request #1943 from amineck/pat_msg

Nicolas De loof 4 年之前
父节点
当前提交
de6bdd4313
共有 3 个文件被更改,包括 175 次插入2 次删除
  1. 6 2
      cli/mobycli/exec.go
  2. 75 0
      cli/mobycli/pat_suggest.go
  3. 94 0
      cli/mobycli/pat_suggest_test.go

+ 6 - 2
cli/mobycli/exec.go

@@ -77,10 +77,14 @@ func Exec(root *cobra.Command) {
 		fmt.Fprintln(os.Stderr, err)
 		os.Exit(1)
 	}
-	command := metrics.GetCommand(os.Args[1:])
-	if command == "build" && !metrics.HasQuietFlag(os.Args[1:]) {
+	commandArgs := os.Args[1:]
+	command := metrics.GetCommand(commandArgs)
+	if command == "build" && !metrics.HasQuietFlag(commandArgs) {
 		utils.DisplayScanSuggestMsg()
 	}
+	if command == "login" && !metrics.HasQuietFlag(commandArgs) {
+		displayPATSuggestMsg(commandArgs)
+	}
 	metrics.Track(store.DefaultContextType, os.Args[1:], compose.SuccessStatus)
 
 	os.Exit(0)

+ 75 - 0
cli/mobycli/pat_suggest.go

@@ -0,0 +1,75 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package mobycli
+
+import (
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/docker/cli/cli/config"
+	"github.com/docker/docker/registry"
+	"github.com/hashicorp/go-uuid"
+)
+
+const (
+	// patSuggestMsg is a message to suggest the use of PAT (personal access tokens).
+	patSuggestMsg = `Logging in with your password grants your terminal complete access to your account. 
+For better security, log in with a limited-privilege personal access token. Learn more at https://docs.docker.com/docker-hub/access-tokens/`
+
+	// patPrefix represents a docker personal access token prefix.
+	patPrefix = "dckrp_"
+)
+
+// displayPATSuggestMsg displays a message suggesting users to use PATs instead of passwords to reduce scope.
+func displayPATSuggestMsg(cmdArgs []string) {
+	if os.Getenv("DOCKER_PAT_SUGGEST") == "false" {
+		return
+	}
+	if !isUsingDefaultRegistry(cmdArgs) {
+		return
+	}
+	authCfg, err := config.LoadDefaultConfigFile(os.Stderr).GetAuthConfig(registry.IndexServer)
+	if err != nil {
+		return
+	}
+	if !isUsingPassword(authCfg.Password) {
+		return
+	}
+	fmt.Fprintf(os.Stderr, "\n"+patSuggestMsg+"\n")
+}
+
+func isUsingDefaultRegistry(cmdArgs []string) bool {
+	for i := 1; i < len(cmdArgs); i++ {
+		if strings.HasPrefix(cmdArgs[i], "-") {
+			i++
+			continue
+		}
+		return cmdArgs[i] == registry.IndexServer
+	}
+	return true
+}
+
+func isUsingPassword(pass string) bool {
+	if _, err := uuid.ParseUUID(pass); err == nil {
+		return false
+	}
+	if strings.HasPrefix(pass, patPrefix) {
+		return false
+	}
+	return true
+}

+ 94 - 0
cli/mobycli/pat_suggest_test.go

@@ -0,0 +1,94 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package mobycli
+
+import (
+	"testing"
+
+	"github.com/docker/docker/registry"
+	"gotest.tools/assert"
+)
+
+func TestIsUsingDefaultRegistry(t *testing.T) {
+	testCases := []struct {
+		name     string
+		input    []string
+		expected bool
+	}{
+		{
+			"without flags",
+			[]string{"login"},
+			true,
+		},
+		{
+			"login with flags",
+			[]string{"login", "-u", "test", "-p", "testpass"},
+			true,
+		},
+		{
+			"login to default registry",
+			[]string{"login", registry.IndexServer},
+			true,
+		},
+		{
+			"login to different registry",
+			[]string{"login", "registry.example.com"},
+			false,
+		},
+		{
+			"login with flags to different registry",
+			[]string{"login", "-u", "test", "-p", "testpass", "registry.example.com"},
+			false,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			result := isUsingDefaultRegistry(testCase.input)
+			assert.Equal(t, testCase.expected, result)
+		})
+	}
+}
+
+func TestIsUsingPassword(t *testing.T) {
+	testCases := []struct {
+		name     string
+		input    string
+		expected bool
+	}{
+		{
+			"regular password",
+			"mypass",
+			true,
+		},
+		{
+			"personal access token",
+			"1508b8bd-b80c-452d-9a7a-ee5607c41bcd",
+			false,
+		},
+		{
+			"prefixed personal access token",
+			"dckrp_ee5607c41bcd",
+			false,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			result := isUsingPassword(testCase.input)
+			assert.Equal(t, testCase.expected, result)
+		})
+	}
+}