github.com/nats-io/jwt/v2@v2.5.6/v1compat/imports.go (about) 1 /* 2 * Copyright 2018-2022 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 ( 19 "io" 20 "net/http" 21 "net/url" 22 "time" 23 ) 24 25 // Import describes a mapping from another account into this one 26 type Import struct { 27 Name string `json:"name,omitempty"` 28 // Subject field in an import is always from the perspective of the 29 // initial publisher - in the case of a stream it is the account owning 30 // the stream (the exporter), and in the case of a service it is the 31 // account making the request (the importer). 32 Subject Subject `json:"subject,omitempty"` 33 Account string `json:"account,omitempty"` 34 Token string `json:"token,omitempty"` 35 // To field in an import is always from the perspective of the subscriber 36 // in the case of a stream it is the client of the stream (the importer), 37 // from the perspective of a service, it is the subscription waiting for 38 // requests (the exporter). If the field is empty, it will default to the 39 // value in the Subject field. 40 To Subject `json:"to,omitempty"` 41 Type ExportType `json:"type,omitempty"` 42 } 43 44 // IsService returns true if the import is of type service 45 func (i *Import) IsService() bool { 46 return i.Type == Service 47 } 48 49 // IsStream returns true if the import is of type stream 50 func (i *Import) IsStream() bool { 51 return i.Type == Stream 52 } 53 54 // Validate checks if an import is valid for the wrapping account 55 func (i *Import) Validate(actPubKey string, vr *ValidationResults) { 56 if i == nil { 57 vr.AddError("null import is not allowed") 58 return 59 } 60 if !i.IsService() && !i.IsStream() { 61 vr.AddError("invalid import type: %q", i.Type) 62 } 63 64 if i.Account == "" { 65 vr.AddError("account to import from is not specified") 66 } 67 68 i.Subject.Validate(vr) 69 70 if i.IsService() && i.Subject.HasWildCards() { 71 vr.AddError("services cannot have wildcard subject: %q", i.Subject) 72 } 73 if i.IsStream() && i.To.HasWildCards() { 74 vr.AddError("streams cannot have wildcard to subject: %q", i.Subject) 75 } 76 77 var act *ActivationClaims 78 79 if i.Token != "" { 80 // Check to see if its an embedded JWT or a URL. 81 if u, err := url.Parse(i.Token); err == nil && u.Scheme != "" { 82 c := &http.Client{Timeout: 5 * time.Second} 83 resp, err := c.Get(u.String()) 84 if err != nil { 85 vr.AddError("import %s contains an unreachable token URL %q", i.Subject, i.Token) 86 } 87 88 if resp != nil { 89 defer resp.Body.Close() 90 body, err := io.ReadAll(resp.Body) 91 if err != nil { 92 vr.AddError("import %s contains an unreadable token URL %q", i.Subject, i.Token) 93 } else { 94 act, err = DecodeActivationClaims(string(body)) 95 if err != nil { 96 vr.AddError("import %s contains a URL %q with an invalid activation token", i.Subject, i.Token) 97 } 98 } 99 } 100 } else { 101 var err error 102 act, err = DecodeActivationClaims(i.Token) 103 if err != nil { 104 vr.AddError("import %q contains an invalid activation token", i.Subject) 105 } 106 } 107 } 108 109 if act != nil { 110 if !(act.Issuer == i.Account || act.IssuerAccount == i.Account) { 111 vr.AddError("activation token doesn't match account for import %q", i.Subject) 112 } 113 if act.ClaimsData.Subject != actPubKey { 114 vr.AddError("activation token doesn't match account it is being included in, %q", i.Subject) 115 } 116 if act.ImportType != i.Type { 117 vr.AddError("mismatch between token import type %s and type of import %s", act.ImportType, i.Type) 118 } 119 act.validateWithTimeChecks(vr, false) 120 subj := i.Subject 121 if i.IsService() && i.To != "" { 122 subj = i.To 123 } 124 if !subj.IsContainedIn(act.ImportSubject) { 125 vr.AddError("activation token import subject %q doesn't match import %q", act.ImportSubject, i.Subject) 126 } 127 } 128 } 129 130 // Imports is a list of import structs 131 type Imports []*Import 132 133 // Validate checks if an import is valid for the wrapping account 134 func (i *Imports) Validate(acctPubKey string, vr *ValidationResults) { 135 toSet := make(map[Subject]bool, len(*i)) 136 for _, v := range *i { 137 if v == nil { 138 vr.AddError("null import is not allowed") 139 continue 140 } 141 if v.Type == Service { 142 if _, ok := toSet[v.To]; ok { 143 vr.AddError("Duplicate To subjects for %q", v.To) 144 } 145 toSet[v.To] = true 146 } 147 v.Validate(acctPubKey, vr) 148 } 149 } 150 151 // Add is a simple way to add imports 152 func (i *Imports) Add(a ...*Import) { 153 *i = append(*i, a...) 154 } 155 156 func (i Imports) Len() int { 157 return len(i) 158 } 159 160 func (i Imports) Swap(j, k int) { 161 i[j], i[k] = i[k], i[j] 162 } 163 164 func (i Imports) Less(j, k int) bool { 165 return i[j].Subject < i[k].Subject 166 }