github.com/kbehouse/nsc@v0.0.6/cmd/nkeyconfigbuilder.go (about)

     1  /*
     2   * Copyright 2018-2019 The NATS Authors
     3   * Licensed under the Apache License, Version 2.0 (the "License");
     4   * you may not use this file except in compliance with the License.
     5   * You may obtain a copy of the License at
     6   *
     7   * http://www.apache.org/licenses/LICENSE-2.0
     8   *
     9   * Unless required by applicable law or agreed to in writing, software
    10   * distributed under the License is distributed on an "AS IS" BASIS,
    11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12   * See the License for the specific language governing permissions and
    13   * limitations under the License.
    14   */
    15  
    16  package cmd
    17  
    18  import (
    19  	"bytes"
    20  	"errors"
    21  	"fmt"
    22  	"strings"
    23  
    24  	"github.com/nats-io/jwt/v2"
    25  )
    26  
    27  type NKeyConfigBuilder struct {
    28  	accounts            accounts
    29  	accountToName       map[string]string
    30  	accountClaims       map[string]*jwt.AccountClaims
    31  	userClaims          map[string][]*jwt.UserClaims
    32  	srcToPrivateImports map[string][]jwt.Import
    33  }
    34  
    35  func NewNKeyConfigBuilder() *NKeyConfigBuilder {
    36  	cb := NKeyConfigBuilder{}
    37  	cb.accounts.Accounts = make(map[string]account)
    38  	cb.accountToName = make(map[string]string)
    39  	cb.accountClaims = make(map[string]*jwt.AccountClaims)
    40  	cb.userClaims = make(map[string][]*jwt.UserClaims)
    41  	cb.srcToPrivateImports = make(map[string][]jwt.Import)
    42  	return &cb
    43  }
    44  
    45  func (cb *NKeyConfigBuilder) SetOutputDir(fp string) error {
    46  	return errors.New("nkey configurations don't support directory output")
    47  }
    48  
    49  func (cb *NKeyConfigBuilder) SetSystemAccount(id string) error {
    50  	return errors.New("nkey configurations don't support system account")
    51  }
    52  
    53  func (cb *NKeyConfigBuilder) Add(rawClaim []byte) error {
    54  	token := string(rawClaim)
    55  	gc, err := jwt.DecodeGeneric(token)
    56  	if err != nil {
    57  		return err
    58  	}
    59  	switch gc.ClaimType() {
    60  	case jwt.AccountClaim:
    61  		ac, err := jwt.DecodeAccountClaims(token)
    62  		if err != nil {
    63  			return err
    64  		}
    65  		cb.AddClaim(ac)
    66  	case jwt.UserClaim:
    67  		uc, err := jwt.DecodeUserClaims(token)
    68  		if err != nil {
    69  			return err
    70  		}
    71  		cb.AddClaim(uc)
    72  	}
    73  	return nil
    74  }
    75  
    76  func (cb *NKeyConfigBuilder) AddClaim(c jwt.Claims) {
    77  	ac, ok := c.(*jwt.AccountClaims)
    78  	if ok {
    79  		cb.addAccountClaim(ac)
    80  		return
    81  	}
    82  	uc, ok := c.(*jwt.UserClaims)
    83  	if ok {
    84  		cb.addUserClaim(uc)
    85  		return
    86  	}
    87  }
    88  
    89  func (cb *NKeyConfigBuilder) addAccountClaim(ac *jwt.AccountClaims) {
    90  	cb.accountClaims[ac.Subject] = ac
    91  	cb.accountToName[ac.Subject] = ac.Name
    92  	for _, i := range ac.Imports {
    93  		if i.Token != "" {
    94  			imps := cb.srcToPrivateImports[i.Account]
    95  			if imps == nil {
    96  				imps = make([]jwt.Import, 0)
    97  			}
    98  			imps = append(imps, *i)
    99  			cb.srcToPrivateImports[i.Account] = imps
   100  		}
   101  	}
   102  }
   103  
   104  func (cb *NKeyConfigBuilder) addUserClaim(uc *jwt.UserClaims) {
   105  	apk := uc.Issuer
   106  	if uc.IssuerAccount != "" {
   107  		apk = uc.IssuerAccount
   108  	}
   109  	users := cb.userClaims[apk]
   110  	if users == nil {
   111  		users = []*jwt.UserClaims{}
   112  	}
   113  	users = append(users, uc)
   114  	cb.userClaims[apk] = users
   115  }
   116  
   117  func (cb *NKeyConfigBuilder) Generate() ([]byte, error) {
   118  	if err := cb.parse(); err != nil {
   119  		return nil, err
   120  	}
   121  	return cb.serialize()
   122  }
   123  
   124  func (cb *NKeyConfigBuilder) parse() error {
   125  	for _, ac := range cb.accountClaims {
   126  		var a account
   127  		users := cb.userClaims[ac.Subject]
   128  		for _, uc := range users {
   129  			a.Users = append(a.Users, user{Nkey: uc.Subject})
   130  		}
   131  
   132  		for _, exports := range ac.Exports {
   133  			var e export
   134  			if exports.IsStream() {
   135  				e.Stream = exports.Subject
   136  			} else {
   137  				e.Service = exports.Subject
   138  			}
   139  
   140  			if exports.TokenReq {
   141  				if e.Accounts == nil {
   142  					accts := make([]string, 0)
   143  					e.Accounts = &accts
   144  				}
   145  
   146  				a := *e.Accounts
   147  				imprts := cb.srcToPrivateImports[ac.Subject]
   148  				for _, imprt := range imprts {
   149  					if imprt.Type == exports.Type && imprt.Subject.IsContainedIn(exports.Subject) {
   150  						ac, err := jwt.DecodeActivationClaims(imprt.Token)
   151  						if err != nil {
   152  							return err
   153  						}
   154  						an := cb.accountToName[ac.Subject]
   155  						a = append(a, an)
   156  					}
   157  
   158  				}
   159  				e.Accounts = &a
   160  			}
   161  			a.Exports = append(a.Exports, e)
   162  		}
   163  
   164  		for _, x := range ac.Imports {
   165  			var e imprt
   166  			var src source
   167  			src.Subject = x.Subject
   168  			src.Account = cb.accountToName[x.Account]
   169  			if src.Account == "" {
   170  				return fmt.Errorf("unable to resolve account %q in import under current operator", x.Account)
   171  			}
   172  			if x.IsStream() {
   173  				e.Stream = &src
   174  				if x.LocalSubject != "" {
   175  					e.LocalSubject = x.LocalSubject
   176  				} else if x.GetTo() != "" {
   177  					e.Prefix = jwt.Subject(x.GetTo())
   178  				}
   179  			} else {
   180  				e.Service = &src
   181  				if x.LocalSubject != "" {
   182  					e.LocalSubject = x.LocalSubject
   183  				} else if x.GetTo() != "" {
   184  					e.To = jwt.Subject(x.GetTo())
   185  				}
   186  			}
   187  			a.Imports = append(a.Imports, e)
   188  		}
   189  
   190  		cb.accounts.Accounts[ac.Name] = a
   191  	}
   192  	return nil
   193  }
   194  
   195  func (cb *NKeyConfigBuilder) serialize() ([]byte, error) {
   196  	return []byte(cb.accounts.String()), nil
   197  }
   198  
   199  type accounts struct {
   200  	Accounts map[string]account `json:"accounts,omitempty"`
   201  }
   202  
   203  func (a *accounts) String() string {
   204  	var buf bytes.Buffer
   205  	buf.WriteString("accounts: {\n")
   206  	for k, v := range a.Accounts {
   207  		buf.WriteString(fmt.Sprintf("  %s: {\n", k))
   208  		buf.WriteString(v.String())
   209  		buf.WriteString("  }\n")
   210  	}
   211  	buf.WriteString("}\n")
   212  	return buf.String()
   213  }
   214  
   215  type account struct {
   216  	Users   []user   `json:"users,omitempty"`
   217  	Exports []export `json:"exports,omitempty"`
   218  	Imports []imprt  `json:"imports,omitempty"`
   219  }
   220  
   221  func (a *account) String() string {
   222  	var buf bytes.Buffer
   223  
   224  	if len(a.Users) > 0 {
   225  		buf.WriteString("    users: [\n")
   226  		for _, u := range a.Users {
   227  			buf.WriteString(fmt.Sprintf("      %s\n", u.String()))
   228  		}
   229  		buf.WriteString("    ]\n")
   230  	}
   231  
   232  	if a.Exports != nil {
   233  		buf.WriteString("    exports: [\n")
   234  		for _, e := range a.Exports {
   235  			buf.WriteString(fmt.Sprintf("      %s\n", e.String()))
   236  		}
   237  		buf.WriteString("    ]\n")
   238  	}
   239  	if len(a.Imports) > 0 {
   240  		buf.WriteString("    imports: [\n")
   241  		for _, i := range a.Imports {
   242  			buf.WriteString(fmt.Sprintf("      %s\n", i.String()))
   243  		}
   244  		buf.WriteString("    ]\n")
   245  	}
   246  	return buf.String()
   247  }
   248  
   249  type user struct {
   250  	Nkey string `json:"nkey,omitempty"`
   251  }
   252  
   253  func (u *user) String() string {
   254  	return fmt.Sprintf("{ nkey: %s }", u.Nkey)
   255  }
   256  
   257  type export struct {
   258  	Stream   jwt.Subject `json:"stream,omitempty"`
   259  	Service  jwt.Subject `json:"service,omitempty"`
   260  	Accounts *[]string   `json:"accounts,omitempty"`
   261  }
   262  
   263  func (ex *export) String() string {
   264  	var buf bytes.Buffer
   265  	buf.WriteString("{ ")
   266  	if ex.Stream != "" {
   267  		buf.WriteString("stream: ")
   268  		buf.WriteString(string(ex.Stream))
   269  	} else {
   270  		buf.WriteString("service: ")
   271  		buf.WriteString(string(ex.Service))
   272  	}
   273  	if ex.Accounts != nil {
   274  		buf.WriteString(", accounts: [")
   275  		buf.WriteString(strings.Join(*ex.Accounts, ","))
   276  		buf.WriteString("]")
   277  	}
   278  	buf.WriteString(" }")
   279  	return buf.String()
   280  }
   281  
   282  type source struct {
   283  	Account string      `json:"account,omitempty"`
   284  	Subject jwt.Subject `json:"subject,omitempty"`
   285  }
   286  
   287  func (s *source) String() string {
   288  	return fmt.Sprintf("{ account: %s, subject: %s }", s.Account, string(s.Subject))
   289  }
   290  
   291  type imprt struct {
   292  	Stream       *source             `json:"stream,omitempty"`
   293  	Service      *source             `json:"service,omitempty"`
   294  	Prefix       jwt.Subject         `json:"prefix,omitempty"`
   295  	To           jwt.Subject         `json:"to,omitempty"`
   296  	LocalSubject jwt.RenamingSubject `json:"local_subject,omitempty"`
   297  }
   298  
   299  func (im *imprt) String() string {
   300  	var buf bytes.Buffer
   301  	buf.WriteString("{ ")
   302  	if im.Service != nil {
   303  		buf.WriteString("service: ")
   304  		buf.WriteString(im.Service.String())
   305  		if im.To != "" {
   306  			buf.WriteString(", to: ")
   307  			buf.WriteString(string(im.To))
   308  		} else if im.LocalSubject != "" {
   309  			buf.WriteString(", to: ")
   310  			buf.WriteString(string(im.LocalSubject))
   311  		}
   312  	} else {
   313  		buf.WriteString("stream: ")
   314  		buf.WriteString(im.Stream.String())
   315  		if im.Prefix != "" {
   316  			buf.WriteString(", prefix: ")
   317  			buf.WriteString(string(im.Prefix))
   318  		} else if im.LocalSubject != "" {
   319  			buf.WriteString(", to: ")
   320  			buf.WriteString(string(im.LocalSubject))
   321  		}
   322  	}
   323  	buf.WriteString("}")
   324  
   325  	return buf.String()
   326  }