Skip to content

[ADDED] Account Support #755

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Oct 1, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 155 additions & 19 deletions server/accounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@
package server

import (
"encoding/base64"
"encoding/json"
"fmt"
"os"
"strings"
"testing"

"github.com/nats-io/nkeys"
)

func simpleAccountServer(t *testing.T) (*Server, *Account, *Account) {
Expand Down Expand Up @@ -45,9 +49,6 @@ func TestRegisterDuplicateAccounts(t *testing.T) {

func TestAccountIsolation(t *testing.T) {
s, fooAcc, barAcc := simpleAccountServer(t)
if fooAcc == nil || barAcc == nil {
t.Fatalf("Error retrieving accounts for 'foo' and 'bar'")
}
cfoo, crFoo, _ := newClientForServer(s)
if err := cfoo.registerWithAccount(fooAcc); err != nil {
t.Fatalf("Error register client with 'foo' account: %v", err)
Expand Down Expand Up @@ -135,10 +136,18 @@ func TestNewAccountsFromClients(t *testing.T) {
opts.AllowNewAccounts = true
s = New(&opts)

c, _, _ = newClientForServer(s)
c, cr, _ = newClientForServer(s)
err := c.parse(connectOp)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should you add a PING after the connect and get the PONG to show that there was no issue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will take a look.

if err != nil {
t.Fatalf("Received an error trying to create an account: %v", err)
t.Fatalf("Received an error trying to connect: %v", err)
}
go c.parse([]byte("PING\r\n"))
l, err = cr.ReadString('\n')
if err != nil {
t.Fatalf("Error reading response for client from server: %v", err)
}
if !strings.HasPrefix(l, "PONG\r\n") {
t.Fatalf("PONG response incorrect: %q", l)
}
}

Expand Down Expand Up @@ -255,7 +264,6 @@ func TestAccountParseConfig(t *testing.T) {
if u.Username == "derek" {
if u.Account != natsAcc {
t.Fatalf("Expected to see the 'nats.io' account, but received %+v", u.Account)
break
}
}
}
Expand Down Expand Up @@ -302,8 +310,7 @@ func TestAccountParseConfigImportsExports(t *testing.T) {
for _, acc := range opts.Accounts {
if acc.Name == "nats.io" {
natsAcc = acc
}
if acc.Name == "synadia" {
} else if acc.Name == "synadia" {
synAcc = acc
}
}
Expand Down Expand Up @@ -420,7 +427,7 @@ func TestImportExportConfigFailures(t *testing.T) {
cf = createConfFile(t, []byte(`
accounts {
nats.io {
exports = [{service: {account: nats.io, subject:"foo.*"}]
exports = [{service: {account: nats.io, subject:"foo.*"}}]
}
}
`))
Expand Down Expand Up @@ -490,6 +497,7 @@ func TestImportAuthorized(t *testing.T) {
}

func TestSimpleMapping(t *testing.T) {
t.Helper()
s, fooAcc, barAcc := simpleAccountServer(t)
defer s.Shutdown()

Expand Down Expand Up @@ -555,7 +563,7 @@ func TestSimpleMapping(t *testing.T) {

l, err = crBar.ReadString('\n')
if err != nil {
t.Fatalf("Error reading from client 'baz': %v", err)
t.Fatalf("Error reading from client 'bar': %v", err)
}
checkMsg(l, "2")
checkPayload(crBar, []byte("hello\r\n"), t)
Expand All @@ -578,11 +586,11 @@ func TestNoPrefixWildcardMapping(t *testing.T) {
t.Fatalf("Error registering client with 'bar' account: %v", err)
}

if err := cfoo.acc.addStreamExport(">", []*Account{barAcc}); err != nil { // Public with no accounts defined.
t.Fatalf("Error adding account export to client foo: %v", err)
if err := cfoo.acc.addStreamExport(">", []*Account{barAcc}); err != nil {
t.Fatalf("Error adding stream export to client foo: %v", err)
}
if err := cbar.acc.addStreamImport(fooAcc, "*", ""); err != nil {
t.Fatalf("Error adding account import to client bar: %v", err)
t.Fatalf("Error adding stream import to client bar: %v", err)
}

// Normal Subscription on bar client for literal "foo".
Expand Down Expand Up @@ -631,11 +639,12 @@ func TestPrefixWildcardMapping(t *testing.T) {
t.Fatalf("Error registering client with 'bar' account: %v", err)
}

if err := cfoo.acc.addStreamExport(">", []*Account{barAcc}); err != nil { // Public with no accounts defined.
t.Fatalf("Error adding account export to client foo: %v", err)
if err := cfoo.acc.addStreamExport(">", []*Account{barAcc}); err != nil {
t.Fatalf("Error adding stream export to client foo: %v", err)
}
// Checking that trailing '.' is accepted, tested that it is auto added above.
if err := cbar.acc.addStreamImport(fooAcc, "*", "pub.imports."); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addStreamImport() checks if prefix's last character is . or not, and if not, adds it. We have tests without the . and here with it, which is good. Maybe a comment above this line to say that . is added automatically but testing that it works if explicitly specified?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

t.Fatalf("Error adding account import to client bar: %v", err)
t.Fatalf("Error adding stream import to client bar: %v", err)
}

// Normal Subscription on bar client for wildcard.
Expand Down Expand Up @@ -684,11 +693,11 @@ func TestPrefixWildcardMappingWithLiteralSub(t *testing.T) {
t.Fatalf("Error registering client with 'bar' account: %v", err)
}

if err := cfoo.acc.addStreamExport(">", []*Account{barAcc}); err != nil { // Public with no accounts defined.
t.Fatalf("Error adding account export to client foo: %v", err)
if err := cfoo.acc.addStreamExport(">", []*Account{barAcc}); err != nil {
t.Fatalf("Error adding stream export to client foo: %v", err)
}
if err := cbar.acc.addStreamImport(fooAcc, "*", "pub.imports."); err != nil {
t.Fatalf("Error adding account import to client bar: %v", err)
t.Fatalf("Error adding stream import to client bar: %v", err)
}

// Normal Subscription on bar client for wildcard.
Expand Down Expand Up @@ -819,6 +828,133 @@ func TestCrossAccountRequestReply(t *testing.T) {
}
}

func TestAccountMapsUsers(t *testing.T) {
// Used for the nkey users to properly sign.
seed1 := "SUAPM67TC4RHQLKBX55NIQXSMATZDOZK6FNEOSS36CAYA7F7TY66LP4BOM"
seed2 := "SUAIS5JPX4X4GJ7EIIJEQ56DH2GWPYJRPWN5XJEDENJOZHCBLI7SEPUQDE"

confFileName := createConfFile(t, []byte(`
accounts {
synadia {
users = [
{user: derek, password: foo},
{nkey: UCNGL4W5QX66CFX6A6DCBVDH5VOHMI7B2UZZU7TXAUQQSI2JPHULCKBR}
]
}
nats {
users = [
{user: ivan, password: bar},
{nkey: UDPGQVFIWZ7Q5UH4I5E6DBCZULQS6VTVBG6CYBD7JV3G3N2GMQOMNAUH}
]
}
}
`))
defer os.Remove(confFileName)
opts, err := ProcessConfigFile(confFileName)
if err != nil {
t.Fatalf("Unexpected error parsing config file: %v", err)
}
s := New(opts)
synadia := s.LookupAccount("synadia")
nats := s.LookupAccount("nats")

if synadia == nil || nats == nil {
t.Fatalf("Expected non nil accounts during lookup")
}

// Make sure a normal log in maps the accounts correctly.
c, _, _ := newClientForServer(s)
connectOp := []byte("CONNECT {\"user\":\"derek\",\"pass\":\"foo\"}\r\n")
c.parse(connectOp)
if c.acc != synadia {
t.Fatalf("Expected the client's account to match 'synadia', got %v", c.acc)
}
// Also test client sublist.
if c.sl != synadia.sl {
t.Fatalf("Expected the client's sublist to match 'synadia' account")
}

c, _, _ = newClientForServer(s)
connectOp = []byte("CONNECT {\"user\":\"ivan\",\"pass\":\"bar\"}\r\n")
c.parse(connectOp)
if c.acc != nats {
t.Fatalf("Expected the client's account to match 'nats', got %v", c.acc)
}
// Also test client sublist.
if c.sl != nats.sl {
t.Fatalf("Expected the client's sublist to match 'nats' account")
}

// Now test nkeys as well.
kp, _ := nkeys.FromSeed(seed1)
pubKey, _ := kp.PublicKey()

c, cr, l := newClientForServer(s)
// Check for Nonce
var info nonceInfo
err = json.Unmarshal([]byte(l[5:]), &info)
if err != nil {
t.Fatalf("Could not parse INFO json: %v\n", err)
}
if info.Nonce == "" {
t.Fatalf("Expected a non-empty nonce with nkeys defined")
}
sigraw, err := kp.Sign([]byte(info.Nonce))
if err != nil {
t.Fatalf("Failed signing nonce: %v", err)
}
sig := base64.StdEncoding.EncodeToString(sigraw)

// PING needed to flush the +OK to us.
cs := fmt.Sprintf("CONNECT {\"nkey\":%q,\"sig\":\"%s\",\"verbose\":true,\"pedantic\":true}\r\nPING\r\n", pubKey, sig)
go c.parse([]byte(cs))
l, _ = cr.ReadString('\n')
if !strings.HasPrefix(l, "+OK") {
t.Fatalf("Expected an OK, got: %v", l)
}
if c.acc != synadia {
t.Fatalf("Expected the nkey client's account to match 'synadia', got %v", c.acc)
}
// Also test client sublist.
if c.sl != synadia.sl {
t.Fatalf("Expected the client's sublist to match 'synadia' account")
}

// Now nats account nkey user.
kp, _ = nkeys.FromSeed(seed2)
pubKey, _ = kp.PublicKey()

c, cr, l = newClientForServer(s)
// Check for Nonce
err = json.Unmarshal([]byte(l[5:]), &info)
if err != nil {
t.Fatalf("Could not parse INFO json: %v\n", err)
}
if info.Nonce == "" {
t.Fatalf("Expected a non-empty nonce with nkeys defined")
}
sigraw, err = kp.Sign([]byte(info.Nonce))
if err != nil {
t.Fatalf("Failed signing nonce: %v", err)
}
sig = base64.StdEncoding.EncodeToString(sigraw)

// PING needed to flush the +OK to us.
cs = fmt.Sprintf("CONNECT {\"nkey\":%q,\"sig\":\"%s\",\"verbose\":true,\"pedantic\":true}\r\nPING\r\n", pubKey, sig)
go c.parse([]byte(cs))
l, _ = cr.ReadString('\n')
if !strings.HasPrefix(l, "+OK") {
t.Fatalf("Expected an OK, got: %v", l)
}
if c.acc != nats {
t.Fatalf("Expected the nkey client's account to match 'nats', got %v", c.acc)
}
// Also test client sublist.
if c.sl != nats.sl {
t.Fatalf("Expected the client's sublist to match 'nats' account")
}
}

func BenchmarkNewRouteReply(b *testing.B) {
opts := defaultServerOptions
s := New(&opts)
Expand Down
5 changes: 4 additions & 1 deletion server/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,13 @@ type serviceImport struct {
ae bool
}

// importMap tracks the imported streams and services.
type importMap struct {
streams map[string]*streamImport
services map[string]*serviceImport // TODO(dlc) sync.Map may be better.
}

// exportMap tracks the exported streams and services.
type exportMap struct {
streams map[string]map[string]*Account
services map[string]map[string]*Account
Expand Down Expand Up @@ -364,7 +366,7 @@ func (s *Server) checkAuthforWarnings() {
}
if warn {
// Warning about using plaintext passwords.
s.Warnf("Plaintext passwords detected. Use Nkeys or Bcrypt passwords in config files.")
s.Warnf("Plaintext passwords detected, use nkeys or bcrypt.")
}
}

Expand Down Expand Up @@ -480,6 +482,7 @@ func (s *Server) isClientAuthorized(c *client) bool {
if err := pub.Verify(c.nonce, sig); err != nil {
return false
}
c.RegisterNkeyUser(nkey)
return true
}

Expand Down
Loading