diff --git a/server/opts.go b/server/opts.go index a7ccd7bd..7ddfd1b4 100644 --- a/server/opts.go +++ b/server/opts.go @@ -136,6 +136,7 @@ type Options struct { TrustedKeys []string `json:"-"` TrustedOperators []*jwt.OperatorClaims `json:"-"` AccountResolver AccountResolver `json:"-"` + resolverPreloads map[string]string CustomClientAuthentication Authentication `json:"-"` CustomRouterAuthentication Authentication `json:"-"` @@ -544,6 +545,24 @@ func (o *Options) ProcessConfigFile(configFile string) error { err := &configErr{tk, fmt.Sprintf("error parsing account resolver, should be MEM or URL(\"url\")")} errors = append(errors, err) } + case "resolver_preload": + mp, ok := v.(map[string]interface{}) + if !ok { + err := &configErr{tk, fmt.Sprintf("preload should be a map of key:jwt")} + errors = append(errors, err) + continue + } + o.resolverPreloads = make(map[string]string) + for key, val := range mp { + tk, val = unwrapValue(val) + if jwt, ok := val.(string); !ok { + err := &configErr{tk, fmt.Sprintf("preload map value should be a string jwt")} + errors = append(errors, err) + continue + } else { + o.resolverPreloads[key] = jwt + } + } case "system_account", "system": if sa, ok := v.(string); !ok { err := &configErr{tk, fmt.Sprintf("system account name must be a string")} diff --git a/server/server.go b/server/server.go index 2a5e2525..de3d6179 100644 --- a/server/server.go +++ b/server/server.go @@ -328,9 +328,18 @@ func (s *Server) configureAccounts() error { for _, acc := range s.opts.Accounts { s.registerAccount(acc) } + // Check for configured account resolvers. if opts.AccountResolver != nil { s.accResolver = opts.AccountResolver + if len(opts.resolverPreloads) > 0 { + if _, ok := s.accResolver.(*MemAccResolver); !ok { + return fmt.Errorf("Resolver preloads only available for MemAccResolver") + } + for k, v := range opts.resolverPreloads { + s.accResolver.Store(k, v) + } + } } // Set the system account if it was configured. diff --git a/test/configs/operator.conf b/test/configs/operator.conf index 6cbc2a80..0131d712 100644 --- a/test/configs/operator.conf +++ b/test/configs/operator.conf @@ -13,4 +13,4 @@ operator = "./configs/nkeys/op.jwt" # E.g. # resolver = URL("https://api.synadia.com/ngs/v1/accounts/jwt") # -resolver = MEMORY \ No newline at end of file +resolver = MEMORY diff --git a/test/configs/resolver_preload.conf b/test/configs/resolver_preload.conf new file mode 100644 index 00000000..1cf910af --- /dev/null +++ b/test/configs/resolver_preload.conf @@ -0,0 +1,26 @@ +# Server that loads an operator JWT + +listen: 127.0.0.1:22222 + +# Can be an array of filenames as well. +# Key can be operator, operators, roots, root, root_operators, root_operator + +operator = "./configs/nkeys/op.jwt" + +system_account = "AD2VB6C25DQPEUUQ7KJBUFX2J4ZNVBPOHSCBISC7VFZXVWXZA7VASQZG" + +# This is for account resolution. +# Can be MEMORY (Testing) or can be URL(url). +# The resolver will append the account name to url for retrieval. +# E.g. +# resolver = URL("https://api.synadia.com/ngs/v1/accounts/jwt") +# +resolver = MEMORY + +# This is a map that can preload keys:jwts into a memory resolver. +resolver_preload = { + AD2VB6C25DQPEUUQ7KJBUFX2J4ZNVBPOHSCBISC7VFZXVWXZA7VASQZG : "eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJDSzU1UERKSUlTWU5QWkhLSUpMVURVVTdJT1dINlM3UkE0RUc2TTVGVUQzUEdGQ1RWWlJRIiwiaWF0IjoxNTQzOTU4NjU4LCJpc3MiOiJPQ0FUMzNNVFZVMlZVT0lNR05HVU5YSjY2QUgyUkxTREFGM01VQkNZQVk1UU1JTDY1TlFNNlhRRyIsInN1YiI6IkFEMlZCNkMyNURRUEVVVVE3S0pCVUZYMko0Wk5WQlBPSFNDQklTQzdWRlpYVldYWkE3VkFTUVpHIiwidHlwZSI6ImFjY291bnQiLCJuYXRzIjp7ImxpbWl0cyI6e319fQ.7m1fysYUsBw15Lj88YmYoHxOI4HlOzu6qgP8Zg-1q9mQXUURijuDGVZrtb7gFYRlo-nG9xZyd2ZTRpMA-b0xCQ" + + ADM2CIIL3RWXBA6T2HW3FODNCQQOUJEHHQD6FKCPVAMHDNTTSMO73ROX: "eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJCMk0zTFRMT1ZNRk03REY3U0M3SE9RTzNXUzI2RFhMTURINk0zRzY3RzRXRFdTWExPNlVBIiwiaWF0IjoxNTQzOTU4NzI0LCJpc3MiOiJPQ0FUMzNNVFZVMlZVT0lNR05HVU5YSjY2QUgyUkxTREFGM01VQkNZQVk1UU1JTDY1TlFNNlhRRyIsInN1YiI6IkFETTJDSUlMM1JXWEJBNlQySFczRk9ETkNRUU9VSkVISFFENkZLQ1BWQU1IRE5UVFNNTzczUk9YIiwidHlwZSI6ImFjY291bnQiLCJuYXRzIjp7ImxpbWl0cyI6e319fQ.pvvPmBei_IFEbspHGN5FkWJoSfHk8BVeJCCVODTgul8-xUU8p1fidvsg3sgMvrXqXtmL8SFc68jGQd0nGtk5Dw" + +} \ No newline at end of file diff --git a/test/operator_test.go b/test/operator_test.go index f292b001..9a79b9b0 100644 --- a/test/operator_test.go +++ b/test/operator_test.go @@ -233,3 +233,20 @@ func TestOperatorSigningKeys(t *testing.T) { } nc.Close() } + +func TestOperatorMemResolverPreload(t *testing.T) { + s, opts := RunServerWithConfig("./configs/resolver_preload.conf") + + // Make sure we can look up the account. + acc := s.LookupAccount("ADM2CIIL3RWXBA6T2HW3FODNCQQOUJEHHQD6FKCPVAMHDNTTSMO73ROX") + if acc == nil { + t.Fatalf("Expected to properly lookup account") + } + sacc := s.SystemAccount() + if sacc == nil { + t.Fatalf("Expected to have system account registered") + } + if sacc.Name != opts.SystemAccount { + t.Fatalf("System account does not match, wanted %q, got %q", opts.SystemAccount, sacc.Name) + } +}