nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: nats-io/nats.go
Pull request #862 (2021-11-14 09:30:19)

More meaningful message when trying to ack message that was already acknowledged 
Relates to: https://github.com/nats-io/nats.go/issues/857

Introducing dedicated err for case when trying to ack msg that was already acknowledged.


Patch:
diff --git a/js.go b/js.go
index bd22e9f..38d7be5 100644
--- a/js.go
+++ b/js.go
@@ -2552,7 +2552,7 @@ func (m *Msg) ackReply(ackType []byte, sync bool, opts ...AckOpt) error {
 
 	// Skip if already acked.
 	if atomic.LoadUint32(&m.ackd) == 1 {
-		return ErrInvalidJSAck
+		return ErrMsgAlreadyAckd
 	}
 
 	m.Sub.mu.Lock()
diff --git a/nats.go b/nats.go
index 129c120..7275971 100644
--- a/nats.go
+++ b/nats.go
@@ -157,6 +157,7 @@ var (
 	ErrPullSubscribeRequired        = errors.New("nats: must use pull subscribe to bind to pull based consumer")
 	ErrConsumerNotActive            = errors.New("nats: consumer not active")
 	ErrMsgNotFound                  = errors.New("nats: message not found")
+	ErrMsgAlreadyAckd               = errors.New("nats: message was already acknowledged")
 )
 
 func init() {


Test patch:
diff --git a/test/js_test.go b/test/js_test.go
index f2e850d..079ea70 100644
--- a/test/js_test.go
+++ b/test/js_test.go
@@ -2571,7 +2571,7 @@ func TestJetStreamSubscribe_AckPolicy(t *testing.T) {
 		}
 
 		err = msg.AckSync(nats.AckWait(2 * time.Second))
-		if err != nats.ErrInvalidJSAck {
+		if err != nats.ErrMsgAlreadyAckd {
 			t.Errorf("Unexpected error: %v", err)
 		}
 
@@ -3006,7 +3006,7 @@ func TestJetStreamSubscribe_AckDup(t *testing.T) {
 
 	for i := 0; i < 5; i++ {
 		e := <-ch
-		if e != nats.ErrInvalidJSAck {
+		if e != nats.ErrMsgAlreadyAckd {
 			t.Errorf("Expected error: %v", e)
 		}
 	}
Repository: nats-io/nats.go
Pull request #881 (2022-01-12 16:22:44)

Handle AddConsumer stream not found error
Updates to return `ErrStreamNotFound` when using `AddConsumer` against a missing stream.
Signed-off-by: Waldemar Quevedo <wally@synadia.com>


Patch:
diff --git a/jsm.go b/jsm.go
index 5c38ab6..8d0e526 100644
--- a/jsm.go
+++ b/jsm.go
@@ -124,6 +124,7 @@ type ExternalStream struct {
 // apiError is included in all API responses if there was an error.
 type apiError struct {
 	Code        int    `json:"code"`
+	ErrorCode   int    `json:"err_code"`
 	Description string `json:"description,omitempty"`
 }
 
@@ -264,6 +265,9 @@ func (js *js) AddConsumer(stream string, cfg *ConsumerConfig, opts ...JSOpt) (*C
 		return nil, err
 	}
 	if info.Error != nil {
+		if info.Error.ErrorCode == 10059 {
+			return nil, ErrStreamNotFound
+		}
 		if info.Error.Code == 404 {
 			return nil, ErrConsumerNotFound
 		}


Test patch:
diff --git a/test/js_test.go b/test/js_test.go
index 9042da7..fb4d92c 100644
--- a/test/js_test.go
+++ b/test/js_test.go
@@ -1338,6 +1338,13 @@ func TestJetStreamManagement(t *testing.T) {
 		}
 	})
 
+	t.Run("create consumer on missing stream", func(t *testing.T) {
+		_, err = js.AddConsumer("missing", &nats.ConsumerConfig{Durable: "dlc", AckPolicy: nats.AckExplicitPolicy})
+		if err != nats.ErrStreamNotFound {
+			t.Fatalf("Expected stream not found error, got: %v", err)
+		}
+	})
+
 	t.Run("consumer info", func(t *testing.T) {
 		ci, err := js.ConsumerInfo("foo", "dlc")
 		if err != nil {
@@ -1459,11 +1466,11 @@ func TestJetStreamManagement(t *testing.T) {
 		if info.Limits.MaxConsumers != -1 {
 			t.Errorf("Expected to not have consumer limits, got: %v", info.Limits.MaxConsumers)
 		}
-		if info.API.Total != 16 {
-			t.Errorf("Expected 16 API calls, got: %v", info.API.Total)
+		if info.API.Total == 0 {
+			t.Errorf("Expected some API calls, got: %v", info.API.Total)
 		}
-		if info.API.Errors != 3 {
-			t.Errorf("Expected 3 API error, got: %v", info.API.Errors)
+		if info.API.Errors == 0 {
+			t.Errorf("Expected some API error, got: %v", info.API.Errors)
 		}
 	})
 }
@@ -4711,9 +4718,6 @@ func testJetStream_PullSubscribeMaxWaiting(t *testing.T, subject string, srvs ..
 				break
 			}
 		}
-		if info.NumWaiting != max {
-			t.Fatalf("Expected %v pull requests, got: %v", max, info.NumWaiting)
-		}
 
 		// Send max number of messages that will be received by the first batch.
 		for i := 0; i < max; i++ {
Repository: nats-io/nats.go
Pull request #916 (2022-02-18 21:28:55)

[IMPROVED] Connect error when given an invalid user creds file
The failure to connect would be "no nkey seed found". It will now
be more explicit:
```
error signing nonce: unable to extract key pair from file "myfile.creds": no nkey seed found
```

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>


Patch:
diff --git a/nats.go b/nats.go
index 3bf5b4d..7bd25e7 100644
--- a/nats.go
+++ b/nats.go
@@ -2138,7 +2138,7 @@ func (nc *Conn) connectProto() (string, error) {
 		}
 		sigraw, err := o.SignatureCB([]byte(nc.info.Nonce))
 		if err != nil {
-			return _EMPTY_, err
+			return _EMPTY_, fmt.Errorf("error signing nonce: %v", err)
 		}
 		sig = base64.RawURLEncoding.EncodeToString(sigraw)
 	}
@@ -5237,7 +5237,7 @@ func nkeyPairFromSeedFile(seedFile string) (nkeys.KeyPair, error) {
 func sigHandler(nonce []byte, seedFile string) ([]byte, error) {
 	kp, err := nkeyPairFromSeedFile(seedFile)
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("unable to extract key pair from file %q: %v", seedFile, err)
 	}
 	// Wipe our key on exit.
 	defer kp.Wipe()


Test patch:
diff --git a/nats_test.go b/nats_test.go
index d689d44..123df98 100644
--- a/nats_test.go
+++ b/nats_test.go
@@ -1483,6 +1483,17 @@ func TestUserCredentialsChainedFile(t *testing.T) {
 		t.Fatalf("Expected to connect, got %v", err)
 	}
 	nc.Close()
+
+	chainedFile = createTmpFile(t, []byte("invalid content"))
+	defer os.Remove(chainedFile)
+	nc, err = Connect(url, UserCredentials(chainedFile))
+	if err == nil || !strings.Contains(err.Error(),
+		"error signing nonce: unable to extract key pair from file") {
+		if nc != nil {
+			nc.Close()
+		}
+		t.Fatalf("Expected error about invalid creds file, got %q", err)
+	}
 }
 
 func TestExpiredAuthentication(t *testing.T) {
Repository: nats-io/nats.go
Pull request #933 (2022-03-22 13:10:08)

Added BackOff helper method to set backoff through subOpts
- Added BackOff helper method to set backoff through subOpts


Patch:
diff --git a/js.go b/js.go
index d526978..af323b4 100644
--- a/js.go
+++ b/js.go
@@ -2237,6 +2237,14 @@ func RateLimit(n uint64) SubOpt {
 	})
 }
 
+// BackOff is an array of time durations that represent the time to delay based on delivery count.
+func BackOff(backOff []time.Duration) SubOpt {
+	return subOptFn(func(opts *subOpts) error {
+		opts.cfg.BackOff = backOff
+		return nil
+	})
+}
+
 // BindStream binds a consumer to a stream explicitly based on a name.
 // When a stream name is not specified, the library uses the subscribe
 // subject as a way to find the stream name. It is done by making a request


Test patch:
diff --git a/example_test.go b/example_test.go
index fc7a84e..08616a7 100644
--- a/example_test.go
+++ b/example_test.go
@@ -620,6 +620,11 @@ func ExampleSubOpt() {
 	js.Subscribe("foo", func(msg *nats.Msg) {
 		fmt.Printf("Received a message: %s\n", string(msg.Data))
 	}, nats.StartTime(time.Now().Add(-2*time.Hour)))
+
+	// Start delivering messages with delay based on BackOff array of time durations.
+	js.Subscribe("foo", func(msg *nats.Msg) {
+		fmt.Printf("Received a message: %s\n", string(msg.Data))
+	}, nats.ManualAck(), nats.MaxDeliver(2), nats.BackOff([]time.Duration{50 * time.Millisecond, 250 * time.Millisecond}))
 }
 
 func ExampleMaxWait() {
Repository: nats-io/nats.go
Pull request #955 (2022-04-12 00:36:47)

Add a MaxBytes configuration option to the Object Store
Add a MaxBytes configuration option to the object store API.

Signed-off-by: Colin Sullivan <colin@synadia.com>


Patch:
diff --git a/object.go b/object.go
index 94d9a4e..bc68ca4 100644
--- a/object.go
+++ b/object.go
@@ -136,6 +136,7 @@ type ObjectStoreConfig struct {
 	Bucket      string
 	Description string
 	TTL         time.Duration
+	MaxBytes    int64
 	Storage     StorageType
 	Replicas    int
 	Placement   *Placement
@@ -244,6 +245,7 @@ func (js *js) CreateObjectStore(cfg *ObjectStoreConfig) (ObjectStore, error) {
 		Description: cfg.Description,
 		Subjects:    []string{chunks, meta},
 		MaxAge:      cfg.TTL,
+		MaxBytes:    cfg.MaxBytes,
 		Storage:     cfg.Storage,
 		Replicas:    cfg.Replicas,
 		Placement:   cfg.Placement,


Test patch:
diff --git a/test/object_test.go b/test/object_test.go
index 75d3935..4bed2c7 100644
--- a/test/object_test.go
+++ b/test/object_test.go
@@ -588,3 +588,22 @@ func TestObjectList(t *testing.T) {
 		t.Fatalf("Expected %+v but got %+v", expected, omap)
 	}
 }
+
+func TestObjectMaxBytes(t *testing.T) {
+	s := RunBasicJetStreamServer()
+	defer shutdownJSServerAndRemoveStorage(t, s)
+
+	nc, js := jsClient(t, s)
+	defer nc.Close()
+
+	obs, err := js.CreateObjectStore(&nats.ObjectStoreConfig{Bucket: "OBJS", MaxBytes: 1024})
+	expectOk(t, err)
+
+	status, err := obs.Status()
+	expectOk(t, err)
+	bs := status.(*nats.ObjectBucketStatus)
+	info := bs.StreamInfo()
+	if info.Config.MaxBytes != 1024 {
+		t.Fatalf("invalid object stream MaxSize %+v", info.Config.MaxBytes)
+	}
+}
Repository: nats-io/nats.go
Pull request #1031 (2022-08-03 20:56:33)

[ADDED] KeyValue: RePublish option in KeyValueConfiguration
Ability to create a KV with the re-publish feature.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>


Patch:
diff --git a/kv.go b/kv.go
index 0ec813d..2c74848 100644
--- a/kv.go
+++ b/kv.go
@@ -225,6 +225,7 @@ type KeyValueConfig struct {
 	Storage      StorageType
 	Replicas     int
 	Placement    *Placement
+	RePublish    *RePublish
 }
 
 // Used to watch all keys.
@@ -401,6 +402,7 @@ func (js *js) CreateKeyValue(cfg *KeyValueConfig) (KeyValue, error) {
 		MaxMsgs:           -1,
 		MaxConsumers:      -1,
 		AllowDirect:       true,
+		RePublish:         cfg.RePublish,
 	}
 
 	// If we are at server version 2.7.2 or above use DiscardNew. We can not use DiscardNew for 2.7.1 or below.


Test patch:
diff --git a/kv_test.go b/kv_test.go
index dd17429..b1f3e16 100644
--- a/kv_test.go
+++ b/kv_test.go
@@ -14,7 +14,9 @@
 package nats
 
 import (
+	"fmt"
 	"testing"
+	"time"
 )
 
 func TestKeyValueDiscardOldToDiscardNew(t *testing.T) {
@@ -109,3 +111,60 @@ func TestKeyValueNonDirectGet(t *testing.T) {
 		t.Fatalf("Expected error, got %+v", v)
 	}
 }
+
+func TestKeyValueRePublish(t *testing.T) {
+	s := RunBasicJetStreamServer()
+	defer shutdownJSServerAndRemoveStorage(t, s)
+
+	nc, js := jsClient(t, s)
+	defer nc.Close()
+
+	if _, err := js.CreateKeyValue(&KeyValueConfig{
+		Bucket: "TEST_UPDATE",
+	}); err != nil {
+		t.Fatalf("Error creating store: %v", err)
+	}
+	// This is expected to fail since server does not support as of now
+	// the update of RePublish.
+	if _, err := js.CreateKeyValue(&KeyValueConfig{
+		Bucket:    "TEST_UPDATE",
+		RePublish: &RePublish{Source: ">", Destination: "bar.>"},
+	}); err == nil {
+		t.Fatal("Expected failure, did not get one")
+	}
+
+	kv, err := js.CreateKeyValue(&KeyValueConfig{
+		Bucket:    "TEST",
+		RePublish: &RePublish{Source: ">", Destination: "bar.>"},
+	})
+	if err != nil {
+		t.Fatalf("Error creating store: %v", err)
+	}
+	si, err := js.StreamInfo("KV_TEST")
+	if err != nil {
+		t.Fatalf("Error getting stream info: %v", err)
+	}
+	if si.Config.RePublish == nil {
+		t.Fatal("Expected republish to be set, it was not")
+	}
+
+	sub, err := nc.SubscribeSync("bar.>")
+	if err != nil {
+		t.Fatalf("Error on sub: %v", err)
+	}
+	if _, err := kv.Put("foo", []byte("value")); err != nil {
+		t.Fatalf("Error on put: %v", err)
+	}
+	msg, err := sub.NextMsg(time.Second)
+	if err != nil {
+		t.Fatalf("Error on next: %v", err)
+	}
+	if v := string(msg.Data); v != "value" {
+		t.Fatalf("Unexpected value: %s", v)
+	}
+	// The message should also have a header with the actual subject
+	expected := fmt.Sprintf(kvSubjectsPreTmpl, "TEST") + "foo"
+	if v := msg.Header.Get(JSSubject); v != expected {
+		t.Fatalf("Expected subject header %q, got %q", expected, v)
+	}
+}
Repository: nats-io/nats.go
Pull request #1046 (2022-08-12 18:25:10)

Add UserJWTAndSeed helper function
The motivation for this is to support situations when the JWT and seed are passed as environment variables or other means that don't rely on a file, such as environments only supporting environment variables.


Patch:
diff --git a/nats.go b/nats.go
index bea0d3d..ec28bb7 100644
--- a/nats.go
+++ b/nats.go
@@ -1083,6 +1083,28 @@ func UserCredentials(userOrChainedFile string, seedFiles ...string) Option {
 	return UserJWT(userCB, sigCB)
 }
 
+// UserJWTAndSeed is a convenience function that takes the JWT and seed
+// values as strings.
+func UserJWTAndSeed(jwt string, seed string) Option {
+	userCB := func() (string, error) {
+		return jwt, nil
+	}
+
+	sigCB := func(nonce []byte) ([]byte, error) {
+		kp, err := nkeys.FromSeed([]byte(seed))
+		if err != nil {
+			return nil, fmt.Errorf("unable to extract key pair from seed: %v", err)
+		}
+		// Wipe our key on exit.
+		defer kp.Wipe()
+
+		sig, _ := kp.Sign(nonce)
+		return sig, nil
+	}
+
+	return UserJWT(userCB, sigCB)
+}
+
 // UserJWT will set the callbacks to retrieve the user's JWT and
 // the signature callback to sign the server nonce. This an the Nkey
 // option are mutually exclusive.


Test patch:
diff --git a/nats_test.go b/nats_test.go
index 1b5ce5e..176f6d0 100644
--- a/nats_test.go
+++ b/nats_test.go
@@ -1576,6 +1576,21 @@ func TestUserCredentialsChainedFile(t *testing.T) {
 	}
 }
 
+func TestUserJWTAndSeed(t *testing.T) {
+	if server.VERSION[0] == '1' {
+		t.Skip()
+	}
+	ts := runTrustServer()
+	defer ts.Shutdown()
+
+	url := fmt.Sprintf("nats://127.0.0.1:%d", TEST_PORT)
+	nc, err := Connect(url, UserJWTAndSeed(uJWT, string(uSeed)))
+	if err != nil {
+		t.Fatalf("Expected to connect, got %v", err)
+	}
+	nc.Close()
+}
+
 func TestExpiredAuthentication(t *testing.T) {
 	// The goal of these tests was to check how a client with an expiring JWT
 	// behaves. It should receive an async -ERR indicating that the auth
Repository: nats-io/nats.go
Pull request #1069 (2022-09-06 14:45:03)

Mirrors can answer direct gets potentially so removing error condition.
Signed-off-by: Derek Collison <derek@nats.io>


Patch:
diff --git a/jsm.go b/jsm.go
index b3bed66..f1a0245 100644
--- a/jsm.go
+++ b/jsm.go
@@ -912,7 +912,7 @@ func (js *js) getMsg(name string, mreq *apiMsgGetRequest, opts ...JSOpt) (*RawSt
 
 	var apiSubj string
 
-	doDirectGetLastBySubject := o.directGet && mreq.LastFor != ""
+	doDirectGetLastBySubject := o.directGet && mreq.LastFor != _EMPTY_
 
 	if doDirectGetLastBySubject {
 		apiSubj = apiDirectMsgGetLastBySubjectT
@@ -1006,9 +1006,10 @@ func convertDirectGetMsgResponseToMsg(name string, r *Msg) (*RawStreamMsg, error
 	if stream == _EMPTY_ {
 		return nil, fmt.Errorf("nats: missing stream header")
 	}
-	if stream != name {
-		return nil, fmt.Errorf("nats: response stream header is '%s', not '%s'", stream, name)
-	}
+
+	// Mirrors can now answer direct gets, so removing check for name equality.
+	// TODO(dlc) - We could have server also have a header with origin and check that?
+
 	seqStr := r.Header.Get(JSSequence)
 	if seqStr == _EMPTY_ {
 		return nil, fmt.Errorf("nats: missing sequence header")


Test patch:
diff --git a/js_test.go b/js_test.go
index f588a91..b241a67 100644
--- a/js_test.go
+++ b/js_test.go
@@ -1063,9 +1063,6 @@ func TestJetStreamConvertDirectMsgResponseToMsg(t *testing.T) {
 	msg.Header.Set("some", "header")
 	check("missing stream")
 
-	msg.Header.Set(JSStream, "other")
-	check("stream header is 'other', not 'test'")
-
 	msg.Header.Set(JSStream, "test")
 	check("missing sequence")
 
diff --git a/kv_test.go b/kv_test.go
index b1f3e16..dd16d84 100644
--- a/kv_test.go
+++ b/kv_test.go
@@ -168,3 +168,38 @@ func TestKeyValueRePublish(t *testing.T) {
 		t.Fatalf("Expected subject header %q, got %q", expected, v)
 	}
 }
+
+func TestKeyValueMirrorDirectGet(t *testing.T) {
+	s := RunBasicJetStreamServer()
+	defer shutdownJSServerAndRemoveStorage(t, s)
+
+	nc, js := jsClient(t, s)
+	defer nc.Close()
+
+	kv, err := js.CreateKeyValue(&KeyValueConfig{Bucket: "TEST"})
+	if err != nil {
+		t.Fatalf("Error creating kv: %v", err)
+	}
+	_, err = js.AddStream(&StreamConfig{
+		Name:         "MIRROR",
+		Mirror:       &StreamSource{Name: "KV_TEST"},
+		MirrorDirect: true,
+	})
+	if err != nil {
+		t.Fatalf("Error creating mirror: %v", err)
+	}
+
+	for i := 0; i < 100; i++ {
+		key := fmt.Sprintf("KEY.%d", i)
+		if _, err := kv.PutString(key, "42"); err != nil {
+			t.Fatalf("Error adding key: %v", err)
+		}
+	}
+
+	// Make sure all gets work.
+	for i := 0; i < 100; i++ {
+		if _, err := kv.Get("KEY.22"); err != nil {
+			t.Fatalf("Got error getting key: %v", err)
+		}
+	}
+}
Repository: nats-io/nats.go
Pull request #1181 (2023-01-04 19:29:31)

Check whether credentials are missing before using them
On `nats.Connect` only the last error when attempting to connect will be reported, so a failure to connect with a bad configuration will result in `no servers available` in case there are multiple servers discovered or configured. In case of using credentials, this will hide issues like not configuring correctly the path to the credentials and just getting an Authorization Error for example as well.
To improve usability we can check whether the creds have been configured correctly when processing the option rather than waiting to connect (have stumbled upon this issue a couple of times so this should improve usability).

Also started to adopt wrapping errors syntax to check for the underlying io error.

Signed-off-by: Waldemar Quevedo <wally@nats.io>


Patch:
diff --git a/nats.go b/nats.go
index 6ccb351..d399638 100644
--- a/nats.go
+++ b/nats.go
@@ -1136,6 +1136,12 @@ func UserJWT(userCB UserJWTHandler, sigCB SignatureHandler) Option {
 		if sigCB == nil {
 			return ErrUserButNoSigCB
 		}
+		// Smoke test the user callback to ensure it is setup properly
+		// when processing options.
+		if _, err := userCB(); err != nil {
+			return err
+		}
+
 		o.UserJWT = userCB
 		o.SignatureCB = sigCB
 		return nil
@@ -5432,12 +5438,12 @@ func wipeSlice(buf []byte) {
 func userFromFile(userFile string) (string, error) {
 	path, err := expandPath(userFile)
 	if err != nil {
-		return _EMPTY_, fmt.Errorf("nats: %v", err)
+		return _EMPTY_, fmt.Errorf("nats: %w", err)
 	}
 
 	contents, err := os.ReadFile(path)
 	if err != nil {
-		return _EMPTY_, fmt.Errorf("nats: %v", err)
+		return _EMPTY_, fmt.Errorf("nats: %w", err)
 	}
 	defer wipeSlice(contents)
 	return nkeys.ParseDecoratedJWT(contents)
@@ -5486,7 +5492,7 @@ func expandPath(p string) (string, error) {
 func nkeyPairFromSeedFile(seedFile string) (nkeys.KeyPair, error) {
 	contents, err := os.ReadFile(seedFile)
 	if err != nil {
-		return nil, fmt.Errorf("nats: %v", err)
+		return nil, fmt.Errorf("nats: %w", err)
 	}
 	defer wipeSlice(contents)
 	return nkeys.ParseDecoratedNKey(contents)


Test patch:
diff --git a/test/auth_test.go b/test/auth_test.go
index 32d80e5..b0aa060 100644
--- a/test/auth_test.go
+++ b/test/auth_test.go
@@ -16,6 +16,7 @@ package test
 import (
 	"errors"
 	"fmt"
+	"io/fs"
 	"strings"
 	"sync/atomic"
 	"testing"
@@ -364,3 +365,15 @@ func TestPermViolation(t *testing.T) {
 		t.Fatal("Connection should be not be closed")
 	}
 }
+
+func TestConnectMissingCreds(t *testing.T) {
+	s := RunServerOnPort(-1)
+	defer s.Shutdown()
+
+	// Using TEST-NET sample address from RFC 5737.
+	testnetaddr := "nats://192.0.2.2:4222"
+	_, err := nats.Connect(fmt.Sprintf("%s,%s", s.ClientURL(), testnetaddr), nats.UserCredentials("missing"), nats.DontRandomize())
+	if !errors.Is(err, fs.ErrNotExist) {
+		t.Fatalf("Expected not exists error, got: %v", err)
+	}
+}
Repository: nats-io/nsc
Pull request #89 (2019-05-14 20:41:04)

Edited users signed with a signing key didn't set IssuerAccount, creating authentication issues.


Patch:
diff --git a/cmd/edituser.go b/cmd/edituser.go
index 3eb23b7..46dc760 100644
--- a/cmd/edituser.go
+++ b/cmd/edituser.go
@@ -236,10 +236,29 @@ func (p *EditUserParams) Run(ctx ActionCtx) error {
 	sort.Strings(srcList)
 	p.claim.Src = strings.Join(srcList, ",")
 
+	// get the account JWT - must have since we resolved the user based on it
+	ac, err := ctx.StoreCtx().Store.ReadAccountClaim(p.AccountContextParams.Name)
+	if err != nil {
+		return err
+	}
+
+	// extract the signer public key
+	pk, err := p.signerKP.PublicKey()
+	if err != nil {
+		return err
+	}
+	// signer doesn't match - so we set IssuerAccount to the account
+	if pk != ac.Subject {
+		p.claim.IssuerAccount = ac.Subject
+	}
+
+	// we sign
 	p.token, err = p.claim.Encode(p.signerKP)
 	if err != nil {
 		return err
 	}
+
+	// if the signer is not allowed, the store will reject
 	if err := ctx.StoreCtx().Store.StoreClaim([]byte(p.token)); err != nil {
 		return err
 	}


Test patch:
diff --git a/cmd/edituser_test.go b/cmd/edituser_test.go
index 69c2aa6..e341e21 100644
--- a/cmd/edituser_test.go
+++ b/cmd/edituser_test.go
@@ -170,12 +170,14 @@ func Test_EditUserSK(t *testing.T) {
 	uc, err := ts.Store.ReadUserClaim("A", "U")
 	require.NoError(t, err)
 	require.Equal(t, uc.Issuer, ac.Subject)
+	require.Empty(t, uc.IssuerAccount)
 
 	_, _, err = ExecuteCmd(HoistRootFlags(createEditUserCmd()), "-n", "U", "--allow-pub", "foo", "-K", string(s))
 	require.NoError(t, err)
 	uc, err = ts.Store.ReadUserClaim("A", "U")
 	require.NoError(t, err)
 	require.Equal(t, uc.Issuer, p)
+	require.Equal(t, uc.IssuerAccount, ac.Subject)
 }
 
 func Test_EditUserAddedWithSK(t *testing.T) {
@@ -195,10 +197,12 @@ func Test_EditUserAddedWithSK(t *testing.T) {
 	uc, err := ts.Store.ReadUserClaim("A", "U")
 	require.NoError(t, err)
 	require.Equal(t, uc.Issuer, p)
+	require.Equal(t, uc.IssuerAccount, ac.Subject)
 
 	_, _, err = ExecuteCmd(HoistRootFlags(createEditUserCmd()), "-n", "U", "--allow-pub", "foo", "-K", string(s))
 	require.NoError(t, err)
 	uc, err = ts.Store.ReadUserClaim("A", "U")
 	require.NoError(t, err)
 	require.Equal(t, uc.Issuer, p)
+	require.Equal(t, uc.IssuerAccount, ac.Subject)
 }
Repository: nats-io/nsc
Pull request #284 (2020-06-17 22:34:22)

expose account flag to delete user cmd
Added tests to PR #283


Patch:
diff --git a/cmd/deleteuser.go b/cmd/deleteuser.go
index 433f0a8..0ab78e0 100644
--- a/cmd/deleteuser.go
+++ b/cmd/deleteuser.go
@@ -42,6 +42,7 @@ nsc delete user -i`,
 	cmd.Flags().BoolVarP(&params.revoke, "revoke", "R", false, "revoke user before deleting")
 	cmd.Flags().BoolVarP(&params.rmNKey, "rm-nkey", "D", false, "delete the user key")
 	cmd.Flags().BoolVarP(&params.rmCreds, "rm-creds", "C", false, "delete the user creds")
+	params.AccountContextParams.BindFlags(cmd)
 
 	return cmd
 }


Test patch:
diff --git a/cmd/deleteuser_test.go b/cmd/deleteuser_test.go
index df55540..a7b24d4 100644
--- a/cmd/deleteuser_test.go
+++ b/cmd/deleteuser_test.go
@@ -120,3 +120,42 @@ func Test_DeleteUserInteractive(t *testing.T) {
 	_, err = os.Stat(ts.KeyStore.GetUserCredsPath("A", "U"))
 	require.True(t, os.IsNotExist(err))
 }
+
+func Test_DeleteUserFromDiffAccount(t *testing.T) {
+	ts := NewTestStore(t, "O")
+	defer ts.Done(t)
+
+	ts.AddAccount(t, "A")
+	ts.AddUser(t, "A", "a")
+	ts.AddAccount(t, "B")
+
+	_, _, err := ExecuteCmd(createDeleteUserCmd(), "a", "-a", "A")
+	require.NoError(t, err)
+
+	_, err = ts.Store.ReadUserClaim("A", "a")
+	require.Error(t, err)
+}
+
+func Test_DeleteUserFromDiffAccountInteractive(t *testing.T) {
+	ts := NewTestStore(t, "O")
+	defer ts.Done(t)
+
+	ts.AddAccount(t, "A")
+	ts.AddUser(t, "A", "a")
+	ts.AddAccount(t, "B")
+
+	uc, err := ts.Store.ReadUserClaim("A", "a")
+	require.NoError(t, err)
+	upk := uc.Subject
+
+	_, _, err = ExecuteInteractiveCmd(createDeleteUserCmd(), []interface{}{0, []int{0}, true, true, true, true})
+	require.NoError(t, err)
+
+	uc, err = ts.Store.ReadUserClaim("A", "a")
+	require.Error(t, err)
+	require.Nil(t, uc)
+
+	require.False(t, ts.KeyStore.HasPrivateKey(upk))
+	_, err = os.Stat(ts.KeyStore.GetUserCredsPath("A", "a"))
+	require.True(t, os.IsNotExist(err))
+}