Skip to content

Commit e0ed405

Browse files
committed
Test ssh-cert:load command
1 parent aff3f2b commit e0ed405

File tree

11 files changed

+177
-109
lines changed

11 files changed

+177
-109
lines changed

tests/integration/app_list_test.go

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"io"
66
"net/http/httptest"
77
"os"
8-
"os/exec"
98
"strings"
109
"testing"
1110

@@ -17,7 +16,7 @@ import (
1716
)
1817

1918
func TestAppList(t *testing.T) {
20-
authServer := mocks.APITokenServer(t)
19+
authServer := mocks.NewAuthServer(t)
2120
defer authServer.Close()
2221

2322
apiHandler := api.NewHandler(t)
@@ -57,22 +56,8 @@ func TestAppList(t *testing.T) {
5756

5857
apiHandler.SetEnvironments(envs)
5958

60-
authenticatedCommand := func(args ...string) *exec.Cmd {
61-
cmd := command(t, args...)
62-
cmd.Env = append(
63-
cmd.Env,
64-
EnvPrefix+"API_BASE_URL="+apiServer.URL,
65-
EnvPrefix+"API_AUTH_URL="+authServer.URL,
66-
EnvPrefix+"TOKEN="+mocks.ValidAPITokens[0],
67-
)
68-
if testing.Verbose() {
69-
cmd.Stderr = os.Stderr
70-
}
71-
return cmd
72-
}
73-
7459
run := func(args ...string) string {
75-
b, err := authenticatedCommand(args...).Output()
60+
b, err := authenticatedCommand(t, apiServer.URL, authServer.URL, args...).Output()
7661
require.NoError(t, err)
7762
return string(b)
7863
}
@@ -90,7 +75,8 @@ app golang:1.23
9075
+--------------+-------------+-------------------+
9176
`, "\n"), run("workers", "-v", "-p", mockProjectID, "-e", "."))
9277

93-
servicesCmd := authenticatedCommand("services", "-p", mockProjectID, "-e", "main")
78+
servicesCmd := authenticatedCommand(t, apiServer.URL, authServer.URL,
79+
"services", "-p", mockProjectID, "-e", "main")
9480
stdErrBuf := bytes.Buffer{}
9581
servicesCmd.Stderr = &stdErrBuf
9682
if testing.Verbose() {

tests/integration/auth_info_test.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
)
1515

1616
func TestAuthInfo(t *testing.T) {
17-
authServer := mocks.APITokenServer(t)
17+
authServer := mocks.NewAuthServer(t)
1818
defer authServer.Close()
1919

2020
apiHandler := api.NewHandler(t)
@@ -37,13 +37,7 @@ func TestAuthInfo(t *testing.T) {
3737
defer apiServer.Close()
3838

3939
run := func(args ...string) string {
40-
cmd := command(t, args...)
41-
cmd.Env = append(
42-
cmd.Env,
43-
EnvPrefix+"API_BASE_URL="+apiServer.URL,
44-
EnvPrefix+"API_AUTH_URL="+authServer.URL,
45-
EnvPrefix+"TOKEN="+mocks.ValidAPITokens[0],
46-
)
40+
cmd := authenticatedCommand(t, apiServer.URL, authServer.URL, args...)
4741
if testing.Verbose() {
4842
cmd.Stderr = os.Stderr
4943
}

tests/integration/environment_list_test.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
)
1616

1717
func TestEnvironmentList(t *testing.T) {
18-
authServer := mocks.APITokenServer(t)
18+
authServer := mocks.NewAuthServer(t)
1919
defer authServer.Close()
2020

2121
apiHandler := api.NewHandler(t)
@@ -36,13 +36,7 @@ func TestEnvironmentList(t *testing.T) {
3636
})
3737

3838
run := func(args ...string) string {
39-
cmd := command(t, args...)
40-
cmd.Env = append(
41-
cmd.Env,
42-
EnvPrefix+"API_BASE_URL="+apiServer.URL,
43-
EnvPrefix+"API_AUTH_URL="+authServer.URL,
44-
EnvPrefix+"TOKEN="+mocks.ValidAPITokens[0],
45-
)
39+
cmd := authenticatedCommand(t, apiServer.URL, authServer.URL, args...)
4640
if testing.Verbose() {
4741
cmd.Stderr = os.Stderr
4842
}

tests/integration/integration_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"path/filepath"
88
"testing"
99

10+
"github.com/platformsh/cli/tests/integration/mocks"
11+
1012
"github.com/stretchr/testify/require"
1113
)
1214

@@ -43,6 +45,17 @@ func command(t *testing.T, args ...string) *exec.Cmd {
4345
return cmd
4446
}
4547

48+
func authenticatedCommand(t *testing.T, apiURL, authURL string, args ...string) *exec.Cmd {
49+
cmd := command(t, args...)
50+
cmd.Env = append(
51+
cmd.Env,
52+
EnvPrefix+"API_BASE_URL="+apiURL,
53+
EnvPrefix+"API_AUTH_URL="+authURL,
54+
EnvPrefix+"TOKEN="+mocks.ValidAPITokens[0],
55+
)
56+
return cmd
57+
}
58+
4659
const EnvPrefix = "TEST_CLI_"
4760

4861
func testEnv() []string {

tests/integration/mocks/api/handler.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package api
33
import (
44
"encoding/json"
55
"net/http"
6+
"strings"
67
"testing"
78

89
"github.com/go-chi/chi/v5"
910
"github.com/go-chi/chi/v5/middleware"
11+
"github.com/stretchr/testify/require"
1012
)
1113

1214
type Handler struct {
@@ -25,6 +27,15 @@ func NewHandler(t *testing.T) *Handler {
2527
h.Mux.Use(middleware.DefaultLogger)
2628
}
2729

30+
h.Mux.Use(func(next http.Handler) http.Handler {
31+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
32+
authHeader := req.Header.Get("Authorization")
33+
require.NotEmpty(t, authHeader)
34+
require.True(t, strings.HasPrefix(authHeader, "Bearer "))
35+
next.ServeHTTP(w, req)
36+
})
37+
})
38+
2839
h.Mux.Get("/users/me", h.handleUsersMe)
2940
h.Mux.Get("/users/{id}/extended-access", h.handleUserExtendedAccess)
3041
h.Mux.Get("/ref/users", h.handleUserRefs)
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package mocks
2+
3+
import (
4+
"crypto/ed25519"
5+
"crypto/rand"
6+
"encoding/json"
7+
"net/http"
8+
"net/http/httptest"
9+
"testing"
10+
"time"
11+
12+
"github.com/stretchr/testify/require"
13+
"golang.org/x/crypto/ssh"
14+
"golang.org/x/exp/slices"
15+
)
16+
17+
var ValidAPITokens = []string{"api-token-1"}
18+
var accessTokens = []string{"access-token-1"}
19+
20+
// NewAuthServer creates a new mock authentication server.
21+
// The caller must call Close() on the server when finished.
22+
func NewAuthServer(t *testing.T) *httptest.Server {
23+
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
24+
if testing.Verbose() {
25+
t.Log(req)
26+
}
27+
if req.Method == http.MethodPost && req.URL.Path == "/oauth2/token" {
28+
require.NoError(t, req.ParseForm())
29+
if gt := req.Form.Get("grant_type"); gt != "api_token" {
30+
w.WriteHeader(http.StatusBadRequest)
31+
_ = json.NewEncoder(w).Encode(map[string]string{"error": "invalid grant type: " + gt})
32+
return
33+
}
34+
apiToken := req.Form.Get("api_token")
35+
if slices.Contains(ValidAPITokens, apiToken) {
36+
_ = json.NewEncoder(w).Encode(struct {
37+
AccessToken string `json:"access_token"`
38+
ExpiresIn int `json:"expires_in"`
39+
Type string `json:"token_type"`
40+
}{AccessToken: accessTokens[0], ExpiresIn: 60, Type: "bearer"})
41+
return
42+
}
43+
w.WriteHeader(http.StatusBadRequest)
44+
_ = json.NewEncoder(w).Encode(map[string]string{"error": "invalid API token"})
45+
return
46+
}
47+
48+
if req.Method == http.MethodPost && req.URL.Path == "/ssh" {
49+
var options struct {
50+
PublicKey string `json:"key"`
51+
}
52+
err := json.NewDecoder(req.Body).Decode(&options)
53+
require.NoError(t, err)
54+
key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(options.PublicKey))
55+
require.NoError(t, err)
56+
signer, err := sshSigner()
57+
require.NoError(t, err)
58+
extensions := make(map[string]string)
59+
60+
// Add standard ssh options
61+
extensions["permit-X11-forwarding"] = ""
62+
extensions["permit-agent-forwarding"] = ""
63+
extensions["permit-port-forwarding"] = ""
64+
extensions["permit-pty"] = ""
65+
extensions["permit-user-rc"] = ""
66+
cert := &ssh.Certificate{
67+
Key: key,
68+
Serial: 0,
69+
CertType: ssh.UserCert,
70+
KeyId: "test-key-id",
71+
ValidAfter: uint64(time.Now().Add(-1 * time.Second).Unix()),
72+
ValidBefore: uint64(time.Now().Add(time.Minute).Unix()),
73+
Permissions: ssh.Permissions{
74+
Extensions: extensions,
75+
},
76+
}
77+
err = cert.SignCert(rand.Reader, signer)
78+
require.NoError(t, err)
79+
_ = json.NewEncoder(w).Encode(struct {
80+
Cert string `json:"certificate"`
81+
}{string(ssh.MarshalAuthorizedKey(cert))})
82+
require.NoError(t, err)
83+
return
84+
}
85+
86+
w.WriteHeader(http.StatusNotFound)
87+
_ = json.NewEncoder(w).Encode(map[string]string{"error": "not found"})
88+
}))
89+
}
90+
91+
func sshSigner() (ssh.Signer, error) {
92+
_, privateKey, err := ed25519.GenerateKey(rand.Reader)
93+
if err != nil {
94+
return nil, err
95+
}
96+
return ssh.NewSignerFromKey(privateKey)
97+
}

tests/integration/mocks/token_server.go

Lines changed: 0 additions & 51 deletions
This file was deleted.

tests/integration/org_list_test.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
)
1616

1717
func TestOrgList(t *testing.T) {
18-
authServer := mocks.APITokenServer(t)
18+
authServer := mocks.NewAuthServer(t)
1919
defer authServer.Close()
2020

2121
makeOrg := func(id, name, label, owner string) *api.Org {
@@ -42,13 +42,7 @@ func TestOrgList(t *testing.T) {
4242
defer apiServer.Close()
4343

4444
run := func(args ...string) string {
45-
cmd := command(t, args...)
46-
cmd.Env = append(
47-
cmd.Env,
48-
EnvPrefix+"API_BASE_URL="+apiServer.URL,
49-
EnvPrefix+"API_AUTH_URL="+authServer.URL,
50-
EnvPrefix+"TOKEN="+mocks.ValidAPITokens[0],
51-
)
45+
cmd := authenticatedCommand(t, apiServer.URL, authServer.URL, args...)
5246
if testing.Verbose() {
5347
cmd.Stderr = os.Stderr
5448
}

tests/integration/project_create_test.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
)
1717

1818
func TestProjectCreate(t *testing.T) {
19-
authServer := mocks.APITokenServer(t)
19+
authServer := mocks.NewAuthServer(t)
2020
defer authServer.Close()
2121

2222
apiHandler := api.NewHandler(t)
@@ -33,13 +33,8 @@ func TestProjectCreate(t *testing.T) {
3333
title := "Test Project Title"
3434
region := "test-region"
3535

36-
cmd := command(t, "project:create", "-v", "--region", region, "--title", title, "--org", "cli-tests")
37-
cmd.Env = append(
38-
cmd.Env,
39-
EnvPrefix+"API_BASE_URL="+apiServer.URL,
40-
EnvPrefix+"API_AUTH_URL="+authServer.URL,
41-
EnvPrefix+"TOKEN="+mocks.ValidAPITokens[0],
42-
)
36+
cmd := authenticatedCommand(t, apiServer.URL, authServer.URL,
37+
"project:create", "-v", "--region", region, "--title", title, "--org", "cli-tests")
4338

4439
var stdErrBuf bytes.Buffer
4540
var stdOutBuf bytes.Buffer

tests/integration/project_list_test.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
)
1515

1616
func TestProjectList(t *testing.T) {
17-
authServer := mocks.APITokenServer(t)
17+
authServer := mocks.NewAuthServer(t)
1818
defer authServer.Close()
1919

2020
myUserID := "my-user-id"
@@ -115,13 +115,7 @@ func TestProjectList(t *testing.T) {
115115
})
116116

117117
run := func(args ...string) string {
118-
cmd := command(t, args...)
119-
cmd.Env = append(
120-
cmd.Env,
121-
EnvPrefix+"API_BASE_URL="+apiServer.URL,
122-
EnvPrefix+"API_AUTH_URL="+authServer.URL,
123-
EnvPrefix+"TOKEN="+mocks.ValidAPITokens[0],
124-
)
118+
cmd := authenticatedCommand(t, apiServer.URL, authServer.URL, args...)
125119
if testing.Verbose() {
126120
cmd.Stderr = os.Stderr
127121
}

0 commit comments

Comments
 (0)