Skip to content

Commit

Permalink
Moved credentials file parsing from jwt to nkey.
Browse files Browse the repository at this point in the history
This allows to avoid unnecessary jwt dependencies.

Signed-off-by: Matthias Hanel <[email protected]>
  • Loading branch information
matthiashanel committed May 29, 2020
1 parent b5fd77f commit 069eed1
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 0 deletions.
78 changes: 78 additions & 0 deletions creds_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package nkeys

import (
"bytes"
"errors"
"regexp"
)

var userConfigRE = regexp.MustCompile(`\s*(?:(?:[-]{3,}.*[-]{3,}\r?\n)([\w\-.=]+)(?:\r?\n[-]{3,}.*[-]{3,}\r?\n))`)

// ParseDecoratedJWT takes a creds file and returns the JWT portion.
func ParseDecoratedJWT(contents []byte) (string, error) {
items := userConfigRE.FindAllSubmatch(contents, -1)
if len(items) == 0 {
return string(contents), nil
}
// First result should be the user JWT.
// We copy here so that if the file contained a seed file too we wipe appropriately.
raw := items[0][1]
tmp := make([]byte, len(raw))
copy(tmp, raw)
return string(tmp), nil
}

// ParseDecoratedNKey takes a creds file, finds the NKey portion and creates a
// key pair from it.
func ParseDecoratedNKey(contents []byte) (KeyPair, error) {
var seed []byte

items := userConfigRE.FindAllSubmatch(contents, -1)
if len(items) > 1 {
seed = items[1][1]
} else {
lines := bytes.Split(contents, []byte("\n"))
for _, line := range lines {
if bytes.HasPrefix(bytes.TrimSpace(line), []byte("SO")) ||
bytes.HasPrefix(bytes.TrimSpace(line), []byte("SA")) ||
bytes.HasPrefix(bytes.TrimSpace(line), []byte("SU")) {
seed = line
break
}
}
}
if seed == nil {
return nil, errors.New("no nkey seed found")
}
if !bytes.HasPrefix(seed, []byte("SO")) &&
!bytes.HasPrefix(seed, []byte("SA")) &&
!bytes.HasPrefix(seed, []byte("SU")) {
return nil, errors.New("doesn't contain a seed nkey")
}
kp, err := FromSeed(seed)
if err != nil {
return nil, err
}
return kp, nil
}

// ParseDecoratedUserNKey takes a creds file, finds the NKey portion and creates a
// key pair from it. Similar to ParseDecoratedNKey but fails for non-user keys.
func ParseDecoratedUserNKey(contents []byte) (KeyPair, error) {
nk, err := ParseDecoratedNKey(contents)
if err != nil {
return nil, err
}
seed, err := nk.Seed()
if err != nil {
return nil, err
}
if !bytes.HasPrefix(seed, []byte("SU")) {
return nil, errors.New("doesn't contain an user seed nkey")
}
kp, err := FromSeed(seed)
if err != nil {
return nil, err
}
return kp, nil
}
82 changes: 82 additions & 0 deletions creds_utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package nkeys

import (
"bytes"
"testing"
)

func Test_ParseDecoratedJWTBad(t *testing.T) {
v, err := ParseDecoratedJWT([]byte("foo"))
if err != nil {
t.Fatal(err)
}
if v != "foo" {
t.Fatal("unexpected input was not returned")
}
}

func Test_ParseDecoratedSeedBad(t *testing.T) {
if _, err := ParseDecoratedNKey([]byte("foo")); err == nil {
t.Fatal("Expected error")
} else if err.Error() != "no nkey seed found"{
t.Fatal(err)
}
}

const(
credsSeed =`SUAOTBNEUHZDFJT3EUMELT7MQTP24JF3XVCXQNDSCU74G5IU6VAJBKH5LI`
credsJwt = `eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJHVDROVU5NRUY3Wk1XQ1JCWFZWVURLUVQ2WllQWjc3VzRKUlFYRDNMMjRIS1VKRUNRSDdRIiwiaWF0IjoxNTkwNzgxNTkzLCJpc3MiOiJBQURXTFRISUNWNFNVQUdGNkVLTlZFVzVCQlA3WVJESUJHV0dHSFo1SkJET1FZQTdHVUZNNkFRVSIsIm5hbWUiOiJPUEVSQVRPUiIsInN1YiI6IlVERTZXVEdMVFRQQ1JKUkpDS0JKUkdWTlpUTElWUjdMRUVFTFI0Q1lXV1dCS0pTN1hZSUtYRFVVIiwibmF0cyI6eyJwdWIiOnt9LCJzdWIiOnt9LCJ0eXBlIjoidXNlciIsInZlcnNpb24iOjJ9fQ.c_XQT04wEoVVNDRjPHeKwe17BOrSpQTcftwIbB7KoNEIz6peZCJDc4-J3emVepHofUOWy7IAo9TlLwYhuGHWAQ`
decoratedCreds = `-----BEGIN NATS USER JWT-----
eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJHVDROVU5NRUY3Wk1XQ1JCWFZWVURLUVQ2WllQWjc3VzRKUlFYRDNMMjRIS1VKRUNRSDdRIiwiaWF0IjoxNTkwNzgxNTkzLCJpc3MiOiJBQURXTFRISUNWNFNVQUdGNkVLTlZFVzVCQlA3WVJESUJHV0dHSFo1SkJET1FZQTdHVUZNNkFRVSIsIm5hbWUiOiJPUEVSQVRPUiIsInN1YiI6IlVERTZXVEdMVFRQQ1JKUkpDS0JKUkdWTlpUTElWUjdMRUVFTFI0Q1lXV1dCS0pTN1hZSUtYRFVVIiwibmF0cyI6eyJwdWIiOnt9LCJzdWIiOnt9LCJ0eXBlIjoidXNlciIsInZlcnNpb24iOjJ9fQ.c_XQT04wEoVVNDRjPHeKwe17BOrSpQTcftwIbB7KoNEIz6peZCJDc4-J3emVepHofUOWy7IAo9TlLwYhuGHWAQ
------END NATS USER JWT------
************************* IMPORTANT *************************
NKEY Seed printed below can be used to sign and prove identity.
NKEYs are sensitive and should be treated as secrets.
-----BEGIN USER NKEY SEED-----
SUAOTBNEUHZDFJT3EUMELT7MQTP24JF3XVCXQNDSCU74G5IU6VAJBKH5LI
------END USER NKEY SEED------
*************************************************************
`
)

func Test_ParseDecoratedSeedAndJWT(t *testing.T) {
// test with and without \r\n
for _, creds := range [][]byte{[]byte(decoratedCreds),
bytes.ReplaceAll([]byte(decoratedCreds), []byte{'\n'}, []byte{'\r', '\n'})} {
kp, err := ParseDecoratedUserNKey(creds)
if err != nil {
t.Fatal(err)
}
pu, err := kp.Seed()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(pu, []byte(credsSeed)) {
t.Fatal("seeds don't match")
}

kp, err = ParseDecoratedNKey(creds)
if err != nil {
t.Fatal(err)
}
pu, err = kp.Seed()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(pu, []byte(credsSeed)) {
t.Fatal("seeds don't match")
}

jwt, err := ParseDecoratedJWT(creds)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal([]byte(jwt), []byte(credsJwt)) {
t.Fatal("jwt don't match")
}
}
}

0 comments on commit 069eed1

Please sign in to comment.