github.com/nats-io/jwt/v2@v2.5.6/imports.go (about)

     1  /*
     2   * Copyright 2018-2020 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 jwt
    17  
    18  // Import describes a mapping from another account into this one
    19  type Import struct {
    20  	Name string `json:"name,omitempty"`
    21  	// Subject field in an import is always from the perspective of the
    22  	// initial publisher - in the case of a stream it is the account owning
    23  	// the stream (the exporter), and in the case of a service it is the
    24  	// account making the request (the importer).
    25  	Subject Subject `json:"subject,omitempty"`
    26  	Account string  `json:"account,omitempty"`
    27  	Token   string  `json:"token,omitempty"`
    28  	// Deprecated: use LocalSubject instead
    29  	// To field in an import is always from the perspective of the subscriber
    30  	// in the case of a stream it is the client of the stream (the importer),
    31  	// from the perspective of a service, it is the subscription waiting for
    32  	// requests (the exporter). If the field is empty, it will default to the
    33  	// value in the Subject field.
    34  	To Subject `json:"to,omitempty"`
    35  	// Local subject used to subscribe (for streams) and publish (for services) to.
    36  	// This value only needs setting if you want to change the value of Subject.
    37  	// If the value of Subject ends in > then LocalSubject needs to end in > as well.
    38  	// LocalSubject can contain $<number> wildcard references where number references the nth wildcard in Subject.
    39  	// The sum of wildcard reference and * tokens needs to match the number of * token in Subject.
    40  	LocalSubject RenamingSubject `json:"local_subject,omitempty"`
    41  	Type         ExportType      `json:"type,omitempty"`
    42  	Share        bool            `json:"share,omitempty"`
    43  	AllowTrace   bool            `json:"allow_trace,omitempty"`
    44  }
    45  
    46  // IsService returns true if the import is of type service
    47  func (i *Import) IsService() bool {
    48  	return i.Type == Service
    49  }
    50  
    51  // IsStream returns true if the import is of type stream
    52  func (i *Import) IsStream() bool {
    53  	return i.Type == Stream
    54  }
    55  
    56  // Returns the value of To without triggering the deprecation warning for a read
    57  func (i *Import) GetTo() string {
    58  	return string(i.To)
    59  }
    60  
    61  // Validate checks if an import is valid for the wrapping account
    62  func (i *Import) Validate(actPubKey string, vr *ValidationResults) {
    63  	if i == nil {
    64  		vr.AddError("null import is not allowed")
    65  		return
    66  	}
    67  	if !i.IsService() && !i.IsStream() {
    68  		vr.AddError("invalid import type: %q", i.Type)
    69  	}
    70  	if i.IsService() && i.AllowTrace {
    71  		vr.AddError("AllowTrace only valid for stream import")
    72  	}
    73  
    74  	if i.Account == "" {
    75  		vr.AddError("account to import from is not specified")
    76  	}
    77  
    78  	if i.GetTo() != "" {
    79  		vr.AddWarning("the field to has been deprecated (use LocalSubject instead)")
    80  	}
    81  
    82  	i.Subject.Validate(vr)
    83  	if i.LocalSubject != "" {
    84  		i.LocalSubject.Validate(i.Subject, vr)
    85  		if i.To != "" {
    86  			vr.AddError("Local Subject replaces To")
    87  		}
    88  	}
    89  
    90  	if i.Share && !i.IsService() {
    91  		vr.AddError("sharing information (for latency tracking) is only valid for services: %q", i.Subject)
    92  	}
    93  	var act *ActivationClaims
    94  
    95  	if i.Token != "" {
    96  		var err error
    97  		act, err = DecodeActivationClaims(i.Token)
    98  		if err != nil {
    99  			vr.AddError("import %q contains an invalid activation token", i.Subject)
   100  		}
   101  	}
   102  
   103  	if act != nil {
   104  		if !(act.Issuer == i.Account || act.IssuerAccount == i.Account) {
   105  			vr.AddError("activation token doesn't match account for import %q", i.Subject)
   106  		}
   107  		if act.ClaimsData.Subject != actPubKey {
   108  			vr.AddError("activation token doesn't match account it is being included in, %q", i.Subject)
   109  		}
   110  		if act.ImportType != i.Type {
   111  			vr.AddError("mismatch between token import type %s and type of import %s", act.ImportType, i.Type)
   112  		}
   113  		act.validateWithTimeChecks(vr, false)
   114  		subj := i.Subject
   115  		if i.IsService() && i.To != "" {
   116  			subj = i.To
   117  		}
   118  		if !subj.IsContainedIn(act.ImportSubject) {
   119  			vr.AddError("activation token import subject %q doesn't match import %q", act.ImportSubject, i.Subject)
   120  		}
   121  	}
   122  }
   123  
   124  // Imports is a list of import structs
   125  type Imports []*Import
   126  
   127  // Validate checks if an import is valid for the wrapping account
   128  func (i *Imports) Validate(acctPubKey string, vr *ValidationResults) {
   129  	toSet := make(map[Subject]struct{}, len(*i))
   130  	for _, v := range *i {
   131  		if v == nil {
   132  			vr.AddError("null import is not allowed")
   133  			continue
   134  		}
   135  		if v.Type == Service {
   136  			sub := v.To
   137  			if sub == "" {
   138  				sub = v.LocalSubject.ToSubject()
   139  			}
   140  			if sub == "" {
   141  				sub = v.Subject
   142  			}
   143  			for k := range toSet {
   144  				if sub.IsContainedIn(k) || k.IsContainedIn(sub) {
   145  					vr.AddError("overlapping subject namespace for %q and %q", sub, k)
   146  				}
   147  			}
   148  			if _, ok := toSet[sub]; ok {
   149  				vr.AddError("overlapping subject namespace for %q", v.To)
   150  			}
   151  			toSet[sub] = struct{}{}
   152  		}
   153  		v.Validate(acctPubKey, vr)
   154  	}
   155  }
   156  
   157  // Add is a simple way to add imports
   158  func (i *Imports) Add(a ...*Import) {
   159  	*i = append(*i, a...)
   160  }
   161  
   162  func (i Imports) Len() int {
   163  	return len(i)
   164  }
   165  
   166  func (i Imports) Swap(j, k int) {
   167  	i[j], i[k] = i[k], i[j]
   168  }
   169  
   170  func (i Imports) Less(j, k int) bool {
   171  	return i[j].Subject < i[k].Subject
   172  }