Initial QSfera import
This commit is contained in:
+177
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2018-2020 The NATS Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package jwt
|
||||
|
||||
// Import describes a mapping from another account into this one
|
||||
type Import struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
// Subject field in an import is always from the perspective of the
|
||||
// initial publisher - in the case of a stream it is the account owning
|
||||
// the stream (the exporter), and in the case of a service it is the
|
||||
// account making the request (the importer).
|
||||
Subject Subject `json:"subject,omitempty"`
|
||||
Account string `json:"account,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
// Deprecated: use LocalSubject instead
|
||||
// To field in an import is always from the perspective of the subscriber
|
||||
// in the case of a stream it is the client of the stream (the importer),
|
||||
// from the perspective of a service, it is the subscription waiting for
|
||||
// requests (the exporter). If the field is empty, it will default to the
|
||||
// value in the Subject field.
|
||||
To Subject `json:"to,omitempty"`
|
||||
// Local subject used to subscribe (for streams) and publish (for services) to.
|
||||
// This value only needs setting if you want to change the value of Subject.
|
||||
// If the value of Subject ends in > then LocalSubject needs to end in > as well.
|
||||
// LocalSubject can contain $<number> wildcard references where number references the nth wildcard in Subject.
|
||||
// The sum of wildcard reference and * tokens needs to match the number of * token in Subject.
|
||||
LocalSubject RenamingSubject `json:"local_subject,omitempty"`
|
||||
Type ExportType `json:"type,omitempty"`
|
||||
Share bool `json:"share,omitempty"`
|
||||
AllowTrace bool `json:"allow_trace,omitempty"`
|
||||
}
|
||||
|
||||
// IsService returns true if the import is of type service
|
||||
func (i *Import) IsService() bool {
|
||||
return i.Type == Service
|
||||
}
|
||||
|
||||
// IsStream returns true if the import is of type stream
|
||||
func (i *Import) IsStream() bool {
|
||||
return i.Type == Stream
|
||||
}
|
||||
|
||||
// Returns the value of To without triggering the deprecation warning for a read
|
||||
func (i *Import) GetTo() string {
|
||||
return string(i.To)
|
||||
}
|
||||
|
||||
// Validate checks if an import is valid for the wrapping account
|
||||
func (i *Import) Validate(actPubKey string, vr *ValidationResults) {
|
||||
if i == nil {
|
||||
vr.AddError("null import is not allowed")
|
||||
return
|
||||
}
|
||||
if !i.IsService() && !i.IsStream() {
|
||||
vr.AddError("invalid import type: %q", i.Type)
|
||||
}
|
||||
if i.IsService() && i.AllowTrace {
|
||||
vr.AddError("AllowTrace only valid for stream import")
|
||||
}
|
||||
|
||||
if i.Account == "" {
|
||||
vr.AddError("account to import from is not specified")
|
||||
}
|
||||
|
||||
if i.GetTo() != "" {
|
||||
vr.AddWarning("the field to has been deprecated (use LocalSubject instead)")
|
||||
}
|
||||
|
||||
i.Subject.Validate(vr)
|
||||
if i.LocalSubject != "" {
|
||||
i.LocalSubject.Validate(i.Subject, vr)
|
||||
if i.To != "" {
|
||||
vr.AddError("Local Subject replaces To")
|
||||
}
|
||||
}
|
||||
|
||||
if i.Share && !i.IsService() {
|
||||
vr.AddError("sharing information (for latency tracking) is only valid for services: %q", i.Subject)
|
||||
}
|
||||
var act *ActivationClaims
|
||||
|
||||
if i.Token != "" {
|
||||
var err error
|
||||
act, err = DecodeActivationClaims(i.Token)
|
||||
if err != nil {
|
||||
vr.AddError("import %q contains an invalid activation token", i.Subject)
|
||||
}
|
||||
}
|
||||
|
||||
if act != nil {
|
||||
if !(act.Issuer == i.Account || act.IssuerAccount == i.Account) {
|
||||
vr.AddError("activation token doesn't match account for import %q", i.Subject)
|
||||
}
|
||||
if act.ClaimsData.Subject != actPubKey {
|
||||
vr.AddError("activation token doesn't match account it is being included in, %q", i.Subject)
|
||||
}
|
||||
if act.ImportType != i.Type {
|
||||
vr.AddError("mismatch between token import type %s and type of import %s", act.ImportType, i.Type)
|
||||
}
|
||||
act.validateWithTimeChecks(vr, false)
|
||||
subj := i.Subject
|
||||
if i.IsService() && i.To != "" {
|
||||
subj = i.To
|
||||
}
|
||||
if !subj.IsContainedIn(act.ImportSubject) {
|
||||
vr.AddError("activation token import subject %q doesn't match import %q", act.ImportSubject, i.Subject)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Imports is a list of import structs
|
||||
type Imports []*Import
|
||||
|
||||
// Validate checks if an import is valid for the wrapping account
|
||||
func (i *Imports) Validate(acctPubKey string, vr *ValidationResults) {
|
||||
// Group subjects by account to check for overlaps only within the same account
|
||||
subsByAcct := make(map[string]map[Subject]struct{}, len(*i))
|
||||
for _, v := range *i {
|
||||
if v == nil {
|
||||
vr.AddError("null import is not allowed")
|
||||
continue
|
||||
}
|
||||
if v.Type == Service {
|
||||
sub := v.To
|
||||
if sub == "" {
|
||||
sub = v.LocalSubject.ToSubject()
|
||||
}
|
||||
if sub == "" {
|
||||
sub = v.Subject
|
||||
}
|
||||
// Check for overlapping subjects only within the same account
|
||||
for subOther := range subsByAcct[v.Account] {
|
||||
if sub.IsContainedIn(subOther) || subOther.IsContainedIn(sub) {
|
||||
vr.AddError("overlapping subject namespace for %q and %q in same account %q", sub, subOther, v.Account)
|
||||
}
|
||||
}
|
||||
if subsByAcct[v.Account] == nil {
|
||||
subsByAcct[v.Account] = make(map[Subject]struct{}, len(*i))
|
||||
}
|
||||
if _, ok := subsByAcct[v.Account][sub]; ok {
|
||||
vr.AddError("overlapping subject namespace for %q in account %q", sub, v.Account)
|
||||
}
|
||||
subsByAcct[v.Account][sub] = struct{}{}
|
||||
}
|
||||
v.Validate(acctPubKey, vr)
|
||||
}
|
||||
}
|
||||
|
||||
// Add is a simple way to add imports
|
||||
func (i *Imports) Add(a ...*Import) {
|
||||
*i = append(*i, a...)
|
||||
}
|
||||
|
||||
func (i Imports) Len() int {
|
||||
return len(i)
|
||||
}
|
||||
|
||||
func (i Imports) Swap(j, k int) {
|
||||
i[j], i[k] = i[k], i[j]
|
||||
}
|
||||
|
||||
func (i Imports) Less(j, k int) bool {
|
||||
return i[j].Subject < i[k].Subject
|
||||
}
|
||||
Reference in New Issue
Block a user