Skip to content

Commit 53d432e

Browse files
author
Aaron Schlesinger
committed
ref(util): move, export and use SSH public key parsing logic
1 parent 32d64f9 commit 53d432e

4 files changed

Lines changed: 82 additions & 13 deletions

File tree

cmd/keys.go

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import (
44
"fmt"
55
"io/ioutil"
66
"path"
7-
"regexp"
87
"strconv"
98
"strings"
109

1110
"github.com/deis/workflow-cli/controller/api"
1211
"github.com/deis/workflow-cli/controller/client"
1312
"github.com/deis/workflow-cli/controller/models/keys"
13+
"github.com/deis/workflow-cli/util"
1414
)
1515

1616
// KeysList lists a user's keys.
@@ -157,22 +157,16 @@ func listKeys() ([]api.KeyCreateRequest, error) {
157157
}
158158

159159
func getKey(filename string) (api.KeyCreateRequest, error) {
160-
regex := regexp.MustCompile("^(ssh-...|ecdsa-[^ ]+) ([^ ]+) ?(.*)")
161-
contents, err := ioutil.ReadFile(filename)
160+
keyContents, err := ioutil.ReadFile(filename)
162161

163162
if err != nil {
164163
return api.KeyCreateRequest{}, err
165164
}
166165

167-
if regex.Match(contents) {
168-
capture := regex.FindStringSubmatch(string(contents))
169-
if capture[3] != "" {
170-
return api.KeyCreateRequest{ID: capture[3], Public: string(contents), Name: filename}, nil
171-
}
172-
173-
id := strings.Split(path.Base(filename), ".")[0]
174-
return api.KeyCreateRequest{ID: id, Public: string(contents), Name: filename}, nil
166+
backupID := strings.Split(path.Base(filename), ".")[0]
167+
keyInfo, err := util.ParseSSHPubKey(backupID, keyContents)
168+
if err != nil {
169+
return api.KeyCreateRequest{}, fmt.Errorf("%s is not a valid ssh key", filename)
175170
}
176-
177-
return api.KeyCreateRequest{}, fmt.Errorf("%s is not a valid ssh key", filename)
171+
return api.KeyCreateRequest{ID: keyInfo.ID, Public: keyInfo.Public, Name: filename}, nil
178172
}

util/doc.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Package util contains various utility functions that are used by both the CLI binary
2+
// and the client SDK
3+
package util

util/ssh.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package util
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
)
7+
8+
var (
9+
sshPubKeyRegex = regexp.MustCompile("^(ssh-...|ecdsa-[^ ]+) ([^ ]+) ?(.*)")
10+
)
11+
12+
// SSHPubKeyInfo contains the information on an SSH public key
13+
type SSHPubKeyInfo struct {
14+
ID string
15+
Public string
16+
}
17+
18+
// ErrInvalidSSHPubKey is the error returned when an SSH public key is unrecognizable
19+
type ErrInvalidSSHPubKey struct {
20+
pubKey []byte
21+
}
22+
23+
// Error is the error interface implementation
24+
func (e ErrInvalidSSHPubKey) Error() string {
25+
return fmt.Sprintf("invalid SSH public key %s", string(e.pubKey))
26+
}
27+
28+
// ErrUnknownSSHPubKeyID is the error returned when an SSH public key is not the expected format
29+
type ErrUnknownSSHPubKeyID struct {
30+
pubKey []byte
31+
}
32+
33+
func (e ErrUnknownSSHPubKeyID) Error() string {
34+
return fmt.Sprintf("unknown SSH public key ID for %s", string(e.pubKey))
35+
}
36+
37+
// ParseSSHPubKey parses a byte slice representation of an SSH Public Key into an
38+
// SSHPubKeyInfo struct. If it cannot find the key ID from the pubKey byte slice itself,
39+
// it uses backupKeyID instead. Returns an appropriate error if parsing failed.
40+
func ParseSSHPubKey(backupKeyID string, pubKey []byte) (*SSHPubKeyInfo, error) {
41+
if !sshPubKeyRegex.Match(pubKey) {
42+
return nil, ErrInvalidSSHPubKey{pubKey: pubKey}
43+
}
44+
capture := sshPubKeyRegex.FindStringSubmatch(string(pubKey))
45+
if len(capture) < 4 || capture[3] == "" {
46+
return &SSHPubKeyInfo{ID: backupKeyID, Public: string(pubKey)}, nil
47+
}
48+
return &SSHPubKeyInfo{ID: capture[3], Public: string(pubKey)}, nil
49+
}

util/ssh_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package util
2+
3+
import (
4+
"testing"
5+
)
6+
7+
const (
8+
pubKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDIqRuNwhjntdJWuAVykr/873X8zzKo6Ms1Vx70BQx0wir8TpaLZEY6CqqKDrMbHZ2Z0ZV2ZITs9fC81GqIdmmDFXZxNfx+B1lSR3ZpmZpQpprtSCevZXihIgy+ND50Hp/wk+3VU54FxhudIlJgpPjb/o5vQFhiyM3ynR5gH3slWVaq9C0TkgXCnTzukGzSTeL7wYPNmLomkrAS0nk0yRfoUZcwmD++HMgEmYlhTbnMlkB3nxzEf/JQxhY6xCHrbtNbRkINCY21dHrsrr/MvBvD8FoKnKpxHX2+HNXZbe7Xl87L9o1OuXrtR1crvq+r+1fPjaynGir07zr9mgJPxouPL/e4ppxTL//vt1kVkWWkh/B+GyXEmP38bQGYMpEA7cAndlxPlOki35JYwDNn5CENQpDp8F4+JsKIzAF1zmkBIA7ngg0cSHHilNgwZXmX7h+7nngLgFIpP8h7A9fCpAKhHUfFUj+Zgl9Xm44+sZOwVBnVijK326TgVDFTUXjE/Xwny+3ERgYwBfOwOKmusNFnS0XHbmh+qa/+D8qge5bKilq48pKHzwngM/U6OwMxmSXTuHclLLen3Ime30TOiPzAhokrVNz/Z3VAkfBuJHby68SAKUgczUEU81wz5wFEt1n1sIJ5V49KMRGaSWb+eWvW81yA7NkDSjnsMLa/IF/ADQ== arschles@gmail.com`
9+
backupKeyID = "mybackup"
10+
)
11+
12+
func TestParseSSHPubKey(t *testing.T) {
13+
info, err := ParseSSHPubKey(backupKeyID, []byte(pubKey))
14+
if err != nil {
15+
t.Fatalf("Error parsing well formed key (%s)", err)
16+
}
17+
if info.ID != "arschles@gmail.com" {
18+
t.Fatalf("expected key ID arschles@gmail.com, got %s", info.ID)
19+
}
20+
if info.Public != pubKey {
21+
t.Fatalf("expected key contents %s, got %s", pubKey, info.Public)
22+
}
23+
}

0 commit comments

Comments
 (0)