Skip to content

Commit

Permalink
Merge pull request #952 from ripienaar/account_push
Browse files Browse the repository at this point in the history
Support pushing an account to native resolver
  • Loading branch information
ripienaar authored Jan 11, 2024
2 parents b468ab4 + b4dcd8c commit 0eef953
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 14 deletions.
96 changes: 96 additions & 0 deletions cli/auth_account_command.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"encoding/json"
"fmt"
"io"
"os"
Expand All @@ -9,6 +10,8 @@ import (
"github.com/AlecAivazis/survey/v2"
"github.com/choria-io/fisk"
"github.com/dustin/go-humanize"
"github.com/fatih/color"
"github.com/nats-io/nats-server/v2/server"
ab "github.com/synadia-io/jwt-auth-builder.go"
)

Expand Down Expand Up @@ -47,6 +50,7 @@ type authAccountCommand struct {
pubDeny []string
subAllow []string
subDeny []string
showJWT bool
}

func configureAuthAccountCommand(auth commandHost) {
Expand Down Expand Up @@ -103,6 +107,11 @@ func configureAuthAccountCommand(auth commandHost) {
rm.Flag("operator", "Operator hosting the account").StringVar(&c.operatorName)
rm.Flag("force", "Removes without prompting").Short('f').UnNegatableBoolVar(&c.force)

push := acct.Command("push", "Push the account to the NATS Resolver").Action(c.pushAction)
push.Arg("name", "Account to act on").StringVar(&c.accountName)
push.Flag("operator", "Operator to act on").StringVar(&c.operatorName)
push.Flag("show", "Show the Account JWT before pushing").UnNegatableBoolVar(&c.showJWT)

sk := acct.Command("keys", "Manage Scoped Signing Keys").Alias("sk").Alias("s")

skadd := sk.Command("add", "Adds a signing key").Alias("new").Alias("a").Alias("n").Action(c.skAddAction)
Expand Down Expand Up @@ -158,6 +167,93 @@ func (c *authAccountCommand) selectOperator(pick bool) (*ab.AuthImpl, ab.Operato
return auth, oper, err
}

// temporary until the server go mod issues are resolved
type serverAPIClaimUpdateResponse struct {
Server *server.ServerInfo `json:"server"`
Data *claimUpdateStatus `json:"data,omitempty"`
Error *claimUpdateError `json:"error,omitempty"`
}

type claimUpdateError struct {
Account string `json:"account,omitempty"`
Code int `json:"code"`
Description string `json:"description,omitempty"`
}

type claimUpdateStatus struct {
Account string `json:"account,omitempty"`
Code int `json:"code,omitempty"`
Message string `json:"message,omitempty"`
}

func (c *authAccountCommand) pushAction(_ *fisk.ParseContext) error {
_, _, acct, err := c.selectAccount(true)
if err != nil {
return err
}

if c.showJWT {
fmt.Printf("Account JWT for %s\n", c.accountName)
fmt.Println()
fmt.Println(acct.JWT())
fmt.Println()
}

nc, _, err := prepareHelper("", natsOpts()...)
if err != nil {
return err
}

expect, _ := currentActiveServers(nc)
if expect > 0 {
fmt.Printf("Updating account %s (%s) on %d server(s)\n", acct.Name(), acct.Subject(), expect)
} else {
fmt.Printf("Updating Account %s (%s) on all servers\n", acct.Name(), acct.Subject())
}
fmt.Println()

errStr := color.RedString("X")
okStr := color.GreenString("✓")
updated := 0
failed := 0

subj := fmt.Sprintf("$SYS.REQ.ACCOUNT.%s.CLAIMS.UPDATE", acct.Subject())
err = doReqAsync(acct.JWT(), subj, expect, nc, func(msg []byte) {
update := serverAPIClaimUpdateResponse{}
err = json.Unmarshal(msg, &update)
if err != nil {
fmt.Printf("%s Invalid JSON response received: %v: %s\n", errStr, err, string(msg))
failed++
return
}

if update.Error != nil {
fmt.Printf("%s Update failed on %s: %v\n", errStr, update.Server.Name, update.Error.Description)
failed++
return
}

fmt.Printf("%s Update of account %s completed on %s\n", okStr, update.Data.Account, update.Server.Name)
updated++
})
if err != nil {
return err
}

if failed > 0 {
if expect > 0 {
return fmt.Errorf("update failed on %d/%d servers", failed, expect)
}
return fmt.Errorf("update failed on %d servers", failed)
}

if updated == 0 {
return fmt.Errorf("no servers were updated")
}

return nil
}

func (c *authAccountCommand) skRmAction(_ *fisk.ParseContext) error {
_, _, acct, err := c.selectAccount(true)
if err != nil {
Expand Down
4 changes: 0 additions & 4 deletions cli/auth_operator_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ func configureAuthOperatorCommand(auth commandHost) {

op := auth.Command("operator", "Manage NATS Operators").Hidden().Alias("o").Alias("op")

// TODO:
//
// - system user
// - require signing keys
add := op.Command("add", "Adds a new Operator").Action(c.addAction)
add.Arg("name", "Unique name for this Operator").StringVar(&c.operatorName)
add.Flag("service", "URLs for the Operator services").PlaceHolder("URL").URLListVar(&c.operatorService)
Expand Down
31 changes: 27 additions & 4 deletions cli/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import (
"github.com/mattn/go-isatty"
"github.com/nats-io/jsm.go"
"github.com/nats-io/jsm.go/api"
"github.com/nats-io/nats-server/v2/server"
"github.com/nats-io/nats.go"
"github.com/nats-io/nuid"
terminal "golang.org/x/term"
Expand Down Expand Up @@ -853,9 +854,14 @@ func doReqAsync(req any, subj string, waitFor int, nc *nats.Conn, cb func([]byte
var err error

if req != nil {
jreq, err = json.MarshalIndent(req, "", " ")
if err != nil {
return err
switch val := req.(type) {
case string:
jreq = []byte(val)
default:
jreq, err = json.Marshal(req)
if err != nil {
return err
}
}
}

Expand Down Expand Up @@ -940,7 +946,7 @@ func doReqAsync(req any, subj string, waitFor int, nc *nats.Conn, cb func([]byte

msg := nats.NewMsg(subj)
msg.Data = jreq
if subj != "$SYS.REQ.SERVER.PING" {
if subj != "$SYS.REQ.SERVER.PING" && !strings.HasPrefix(subj, "$SYS.REQ.ACCOUNT") {
msg.Header.Set("Accept-Encoding", "snappy")
}
msg.Reply = sub.Subject
Expand Down Expand Up @@ -1497,3 +1503,20 @@ func xdgShareHome() (string, error) {

return filepath.Join(u.HomeDir, ".local", "share"), nil
}

func currentActiveServers(nc *nats.Conn) (int, error) {
var expect int

err := doReqAsync(nil, "$SYS.REQ.SERVER.PING", 1, nc, func(msg []byte) {
var res server.ServerStatsMsg

err := json.Unmarshal(msg, &res)
if err != nil {
return
}

expect = res.Stats.ActiveServers
})

return expect, err
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ require (
github.com/mattn/go-isatty v0.0.20
github.com/nats-io/jsm.go v0.1.1-0.20240108123425-6d6040d69966
github.com/nats-io/jwt/v2 v2.5.3
github.com/nats-io/nats-server/v2 v2.10.7
github.com/nats-io/nats-server/v2 v2.10.9
github.com/nats-io/nats.go v1.31.0
github.com/nats-io/nkeys v0.4.7
github.com/nats-io/nuid v1.0.1
github.com/prometheus/client_golang v1.18.0
github.com/prometheus/common v0.45.0
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/synadia-io/jwt-auth-builder.go v0.0.0-20240110133806-b7e1342a16b5
github.com/synadia-io/jwt-auth-builder.go v0.0.0-20240110145530-7404d7ddb699
github.com/tylertreat/hdrhistogram-writer v0.0.0-20210816161836-2e440612a39f
golang.org/x/crypto v0.18.0
golang.org/x/term v0.16.0
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ github.com/nats-io/jsm.go v0.1.1-0.20240108123425-6d6040d69966 h1:vQ81ECrRyCxlcT
github.com/nats-io/jsm.go v0.1.1-0.20240108123425-6d6040d69966/go.mod h1:P05AY49lc6LKk4YluJlncmROwC0ZVWKXDCICXlnxREs=
github.com/nats-io/jwt/v2 v2.5.3 h1:/9SWvzc6hTfamcgXJ3uYRpgj+QuY2aLNqRiqrKcrpEo=
github.com/nats-io/jwt/v2 v2.5.3/go.mod h1:iysuPemFcc7p4IoYots3IuELSI4EDe9Y0bQMe+I3Bf4=
github.com/nats-io/nats-server/v2 v2.10.7 h1:f5VDy+GMu7JyuFA0Fef+6TfulfCs5nBTgq7MMkFJx5Y=
github.com/nats-io/nats-server/v2 v2.10.7/go.mod h1:V2JHOvPiPdtfDXTuEUsthUnCvSDeFrK4Xn9hRo6du7c=
github.com/nats-io/nats-server/v2 v2.10.9 h1:VEW43Zz+p+9lARtiPM9ctd6ckun+92ZT2T17HWtwiFI=
github.com/nats-io/nats-server/v2 v2.10.9/go.mod h1:oorGiV9j3BOLLO3ejQe+U7pfAGyPo+ppD7rpgNF6KTQ=
github.com/nats-io/nats.go v1.31.0 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E=
github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8=
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
Expand Down Expand Up @@ -114,8 +114,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/synadia-io/jwt-auth-builder.go v0.0.0-20240110133806-b7e1342a16b5 h1:lctV57uDy0SbGeXdGqQIy2qcofbCBwqkztbX+8UE7SU=
github.com/synadia-io/jwt-auth-builder.go v0.0.0-20240110133806-b7e1342a16b5/go.mod h1:3cZKwBfn+gN7MCMlk49JSpVD4yj1eOqX7jr9sCWtwys=
github.com/synadia-io/jwt-auth-builder.go v0.0.0-20240110145530-7404d7ddb699 h1:UeW+3v3XMO4HFazfKcUBjIC3MyWYT3XPl9p6h6sOCks=
github.com/synadia-io/jwt-auth-builder.go v0.0.0-20240110145530-7404d7ddb699/go.mod h1:3cZKwBfn+gN7MCMlk49JSpVD4yj1eOqX7jr9sCWtwys=
github.com/tylertreat/hdrhistogram-writer v0.0.0-20210816161836-2e440612a39f h1:SGznmvCovewbaSgBsHgdThtWsLj5aCLX/3ZXMLd1UD0=
github.com/tylertreat/hdrhistogram-writer v0.0.0-20210816161836-2e440612a39f/go.mod h1:IY84XkhrEJTdHYLNy/zObs8mXuUAp9I65VyarbPSCCY=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
Expand Down

0 comments on commit 0eef953

Please sign in to comment.