github.com/nats-io/jwt/v2@v2.5.6/v1compat/operator_claims.go (about) 1 /* 2 * Copyright 2018 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 "errors" 20 "fmt" 21 "net/url" 22 "strings" 23 24 "github.com/nats-io/nkeys" 25 ) 26 27 // Operator specific claims 28 type Operator struct { 29 // Slice of real identities (like websites) that can be used to identify the operator. 30 Identities []Identity `json:"identity,omitempty"` 31 // Slice of other operator NKeys that can be used to sign on behalf of the main 32 // operator identity. 33 SigningKeys StringList `json:"signing_keys,omitempty"` 34 // AccountServerURL is a partial URL like "https://host.domain.org:<port>/jwt/v1" 35 // tools will use the prefix and build queries by appending /accounts/<account_id> 36 // or /operator to the path provided. Note this assumes that the account server 37 // can handle requests in a nats-account-server compatible way. See 38 // https://github.com/nats-io/nats-account-server. 39 AccountServerURL string `json:"account_server_url,omitempty"` 40 // A list of NATS urls (tls://host:port) where tools can connect to the server 41 // using proper credentials. 42 OperatorServiceURLs StringList `json:"operator_service_urls,omitempty"` 43 // Identity of the system account 44 SystemAccount string `json:"system_account,omitempty"` 45 } 46 47 // Validate checks the validity of the operators contents 48 func (o *Operator) Validate(vr *ValidationResults) { 49 if err := o.validateAccountServerURL(); err != nil { 50 vr.AddError(err.Error()) 51 } 52 53 for _, v := range o.validateOperatorServiceURLs() { 54 if v != nil { 55 vr.AddError(v.Error()) 56 } 57 } 58 59 for _, i := range o.Identities { 60 i.Validate(vr) 61 } 62 63 for _, k := range o.SigningKeys { 64 if !nkeys.IsValidPublicOperatorKey(k) { 65 vr.AddError("%s is not an operator public key", k) 66 } 67 } 68 if o.SystemAccount != "" { 69 if !nkeys.IsValidPublicAccountKey(o.SystemAccount) { 70 vr.AddError("%s is not an account public key", o.SystemAccount) 71 } 72 } 73 } 74 75 func (o *Operator) validateAccountServerURL() error { 76 if o.AccountServerURL != "" { 77 // We don't care what kind of URL it is so long as it parses 78 // and has a protocol. The account server may impose additional 79 // constraints on the type of URLs that it is able to notify to 80 u, err := url.Parse(o.AccountServerURL) 81 if err != nil { 82 return fmt.Errorf("error parsing account server url: %v", err) 83 } 84 if u.Scheme == "" { 85 return fmt.Errorf("account server url %q requires a protocol", o.AccountServerURL) 86 } 87 } 88 return nil 89 } 90 91 // ValidateOperatorServiceURL returns an error if the URL is not a valid NATS or TLS url. 92 func ValidateOperatorServiceURL(v string) error { 93 // should be possible for the service url to not be expressed 94 if v == "" { 95 return nil 96 } 97 u, err := url.Parse(v) 98 if err != nil { 99 return fmt.Errorf("error parsing operator service url %q: %v", v, err) 100 } 101 102 if u.User != nil { 103 return fmt.Errorf("operator service url %q - credentials are not supported", v) 104 } 105 106 if u.Path != "" { 107 return fmt.Errorf("operator service url %q - paths are not supported", v) 108 } 109 110 lcs := strings.ToLower(u.Scheme) 111 switch lcs { 112 case "nats": 113 return nil 114 case "tls": 115 return nil 116 default: 117 return fmt.Errorf("operator service url %q - protocol not supported (only 'nats' or 'tls' only)", v) 118 } 119 } 120 121 func (o *Operator) validateOperatorServiceURLs() []error { 122 var errs []error 123 for _, v := range o.OperatorServiceURLs { 124 if v != "" { 125 if err := ValidateOperatorServiceURL(v); err != nil { 126 errs = append(errs, err) 127 } 128 } 129 } 130 return errs 131 } 132 133 // OperatorClaims define the data for an operator JWT 134 type OperatorClaims struct { 135 ClaimsData 136 Operator `json:"nats,omitempty"` 137 } 138 139 // NewOperatorClaims creates a new operator claim with the specified subject, which should be an operator public key 140 func NewOperatorClaims(subject string) *OperatorClaims { 141 if subject == "" { 142 return nil 143 } 144 c := &OperatorClaims{} 145 c.Subject = subject 146 return c 147 } 148 149 // DidSign checks the claims against the operator's public key and its signing keys 150 func (oc *OperatorClaims) DidSign(op Claims) bool { 151 if op == nil { 152 return false 153 } 154 issuer := op.Claims().Issuer 155 if issuer == oc.Subject { 156 return true 157 } 158 return oc.SigningKeys.Contains(issuer) 159 } 160 161 // Deprecated: AddSigningKey, use claim.SigningKeys.Add() 162 func (oc *OperatorClaims) AddSigningKey(pk string) { 163 oc.SigningKeys.Add(pk) 164 } 165 166 // Encode the claims into a JWT string 167 func (oc *OperatorClaims) Encode(pair nkeys.KeyPair) (string, error) { 168 if !nkeys.IsValidPublicOperatorKey(oc.Subject) { 169 return "", errors.New("expected subject to be an operator public key") 170 } 171 err := oc.validateAccountServerURL() 172 if err != nil { 173 return "", err 174 } 175 oc.ClaimsData.Type = OperatorClaim 176 return oc.ClaimsData.Encode(pair, oc) 177 } 178 179 // DecodeOperatorClaims tries to create an operator claims from a JWt string 180 func DecodeOperatorClaims(token string) (*OperatorClaims, error) { 181 v := OperatorClaims{} 182 if err := Decode(token, &v); err != nil { 183 return nil, err 184 } 185 return &v, nil 186 } 187 188 func (oc *OperatorClaims) String() string { 189 return oc.ClaimsData.String(oc) 190 } 191 192 // Payload returns the operator specific data for an operator JWT 193 func (oc *OperatorClaims) Payload() interface{} { 194 return &oc.Operator 195 } 196 197 // Validate the contents of the claims 198 func (oc *OperatorClaims) Validate(vr *ValidationResults) { 199 oc.ClaimsData.Validate(vr) 200 oc.Operator.Validate(vr) 201 } 202 203 // ExpectedPrefixes defines the nkey types that can sign operator claims, operator 204 func (oc *OperatorClaims) ExpectedPrefixes() []nkeys.PrefixByte { 205 return []nkeys.PrefixByte{nkeys.PrefixByteOperator} 206 } 207 208 // Claims returns the generic claims data 209 func (oc *OperatorClaims) Claims() *ClaimsData { 210 return &oc.ClaimsData 211 }