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 }