Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Adding] support for account_token_position #1874

Merged
merged 2 commits into from
Feb 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, any reason for changes in the go.sum? Could you make sure that you have rebased from master and that these changes are required?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe missing in an earlier commit?

> git co account_protected_export
Switched to branch 'account_protected_export'
> git rebase origin/master
Current branch account_protected_export is up to date.
> go mod vendor
> go mod tidy
> git status
On branch account_protected_export
Untracked files:
  (use "git add <file>..." to include in what will be committed)
	mytest
	server.test
nothing added to commit but untracked files present (use "git add" to track)
>

github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
Expand All @@ -27,10 +29,6 @@ github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5H
github.com/nats-io/nats.go v1.10.1-0.20200531124210-96f2130e4d55/go.mod h1:ARiFsjW9DVxk48WJbO3OSZ2DG8fjkMi7ecLmXoY/n9I=
github.com/nats-io/nats.go v1.10.1-0.20200606002146-fc6fed82929a/go.mod h1:8eAIv96Mo9QW6Or40jUHejS7e4VwZ3VRYD6Sf0BTDp4=
github.com/nats-io/nats.go v1.10.1-0.20201021145452-94be476ad6e0/go.mod h1:VU2zERjp8xmF+Lw2NH4u2t5qWZxwc7jB3+7HVMWQXPI=
github.com/nats-io/nats.go v1.10.1-0.20210122204956-b8ea7fc17ea6 h1:cpS+9uyfHXvRG/Q+WcDd3KXRgPa9fo9tDbIeDHCxYAg=
github.com/nats-io/nats.go v1.10.1-0.20210122204956-b8ea7fc17ea6/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
github.com/nats-io/nats.go v1.10.1-0.20210123004354-58bf69ad2df8 h1:yxExhj0DStfAEN5lGy6pyL4WJE+J8aKn50xoKt9hFdA=
github.com/nats-io/nats.go v1.10.1-0.20210123004354-58bf69ad2df8/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
github.com/nats-io/nats.go v1.10.1-0.20210127212649-5b4924938a9a h1:EjwBk6T/arS7o0ZGdMgdzYrQHeUITT1GHf3cFQFtr3I=
github.com/nats-io/nats.go v1.10.1-0.20210127212649-5b4924938a9a/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
Expand All @@ -54,10 +52,12 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
127 changes: 79 additions & 48 deletions server/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,9 @@ func (rt ServiceRespType) String() string {
// exportAuth holds configured approvals or boolean indicating an
// auth token is required for import.
type exportAuth struct {
tokenReq bool
approved map[string]*Account
tokenReq bool
accountPos uint
approved map[string]*Account
}

// streamExport
Expand Down Expand Up @@ -877,13 +878,49 @@ func (a *Account) removeClient(c *client) int {
return n
}

func setExportAuth(ea *exportAuth, subject string, accounts []*Account, accountPos uint) error {
if accountPos > 0 {
token := strings.Split(subject, ".")
if len(token) < int(accountPos) || token[accountPos-1] != "*" {
return ErrInvalidSubject
}
}
ea.accountPos = accountPos
// empty means auth required but will be import token.
if accounts == nil {
matthiashanel marked this conversation as resolved.
Show resolved Hide resolved
return nil
}
if len(accounts) == 0 {
ea.tokenReq = true
return nil
}
if ea.approved == nil {
ea.approved = make(map[string]*Account, len(accounts))
}
for _, acc := range accounts {
ea.approved[acc.Name] = acc
}
return nil
}

// AddServiceExport will configure the account with the defined export.
func (a *Account) AddServiceExport(subject string, accounts []*Account) error {
return a.AddServiceExportWithResponse(subject, Singleton, accounts)
return a.addServiceExportWithResponseAndAccountPos(subject, Singleton, accounts, 0)
}

// AddServiceExport will configure the account with the defined export.
func (a *Account) addServiceExportWithAccountPos(subject string, accounts []*Account, accountPos uint) error {
return a.addServiceExportWithResponseAndAccountPos(subject, Singleton, accounts, accountPos)
}

// AddServiceExportWithResponse will configure the account with the defined export and response type.
func (a *Account) AddServiceExportWithResponse(subject string, respType ServiceRespType, accounts []*Account) error {
return a.addServiceExportWithResponseAndAccountPos(subject, respType, accounts, 0)
}

// AddServiceExportWithresponse will configure the account with the defined export and response type.
func (a *Account) addServiceExportWithResponseAndAccountPos(
subject string, respType ServiceRespType, accounts []*Account, accountPos uint) error {
if a == nil {
return ErrMissingAccount
}
Expand All @@ -905,17 +942,12 @@ func (a *Account) AddServiceExportWithResponse(subject string, respType ServiceR
se.respType = respType
}

if accounts != nil {
// empty means auth required but will be import token.
if len(accounts) == 0 {
se.tokenReq = true
} else {
if se.approved == nil {
se.approved = make(map[string]*Account, len(accounts))
}
for _, acc := range accounts {
se.approved[acc.Name] = acc
}
if accounts != nil || accountPos > 0 {
if se == nil {
se = &serviceExport{}
}
if err := setExportAuth(&se.exportAuth, subject, accounts, accountPos); err != nil {
return err
}
}
lrt := a.lowestServiceExportResponseTime()
Expand Down Expand Up @@ -2280,8 +2312,16 @@ func (a *Account) AddStreamImport(account *Account, from, prefix string) error {
var IsPublicExport = []*Account(nil)

// AddStreamExport will add an export to the account. If accounts is nil
// it will signify a public export, meaning anyone can impoort.
// it will signify a public export, meaning anyone can import.
func (a *Account) AddStreamExport(subject string, accounts []*Account) error {
return a.addStreamExportWithAccountPos(subject, accounts, 0)
}

// AddStreamExport will add an export to the account. If accounts is nil
// it will signify a public export, meaning anyone can import.
// if accountPos is > 0, all imports will be granted where the following holds:
// strings.Split(subject, ".")[accountPos] == account id will be granted.
func (a *Account) addStreamExportWithAccountPos(subject string, accounts []*Account, accountPos uint) error {
if a == nil {
return ErrMissingAccount
}
Expand All @@ -2293,20 +2333,12 @@ func (a *Account) AddStreamExport(subject string, accounts []*Account) error {
a.exports.streams = make(map[string]*streamExport)
}
ea := a.exports.streams[subject]
if accounts != nil {
if accounts != nil || accountPos > 0 {
if ea == nil {
ea = &streamExport{}
}
// empty means auth required but will be import token.
if len(accounts) == 0 {
ea.tokenReq = true
} else {
if ea.approved == nil {
ea.approved = make(map[string]*Account, len(accounts))
}
for _, acc := range accounts {
ea.approved[acc.Name] = acc
}
if err := setExportAuth(&ea.exportAuth, subject, accounts, accountPos); err != nil {
return err
}
}
a.exports.streams[subject] = ea
Expand All @@ -2329,15 +2361,22 @@ func (a *Account) checkStreamImportAuthorizedNoLock(account *Account, subject st
return a.checkStreamExportApproved(account, subject, imClaim)
}

func (a *Account) checkAuth(ea *exportAuth, account *Account, imClaim *jwt.Import) bool {
func (a *Account) checkAuth(ea *exportAuth, account *Account, imClaim *jwt.Import, tokens []string) bool {
// if ea is nil or ea.approved is nil, that denotes a public export
if ea == nil || (ea.approved == nil && !ea.tokenReq) {
if ea == nil || (ea.approved == nil && !ea.tokenReq && ea.accountPos == 0) {
return true
}
// Check if the export is protected and enforces presence of importing account identity
if ea.accountPos > 0 {
return ea.accountPos <= uint(len(tokens)) && tokens[ea.accountPos-1] == account.Name
}
// Check if token required
if ea.tokenReq {
return a.checkActivation(account, imClaim, true)
}
if ea.approved == nil {
return false
}
// If we have a matching account we are authorized
_, ok := ea.approved[account.Name]
return ok
Expand All @@ -2347,10 +2386,11 @@ func (a *Account) checkStreamExportApproved(account *Account, subject string, im
// Check direct match of subject first
ea, ok := a.exports.streams[subject]
if ok {
// if ea is nil or eq.approved is nil, that denotes a public export
if ea == nil {
return true
}
return a.checkAuth(&ea.exportAuth, account, imClaim)
return a.checkAuth(&ea.exportAuth, account, imClaim, nil)
}
// ok if we are here we did not match directly so we need to test each one.
// The import subject arg has to take precedence, meaning the export
Expand All @@ -2362,7 +2402,7 @@ func (a *Account) checkStreamExportApproved(account *Account, subject string, im
if ea == nil {
return true
}
return a.checkAuth(&ea.exportAuth, account, imClaim)
return a.checkAuth(&ea.exportAuth, account, imClaim, tokens)
}
}
return false
Expand All @@ -2372,17 +2412,11 @@ func (a *Account) checkServiceExportApproved(account *Account, subject string, i
// Check direct match of subject first
se, ok := a.exports.services[subject]
if ok {
// if se is nil or eq.approved is nil, that denotes a public export
if se == nil || (se.approved == nil && !se.tokenReq) {
// if ea is nil or eq.approved is nil, that denotes a public export
if se == nil {
return true
}
// Check if token required
if se.tokenReq {
return a.checkActivation(account, imClaim, true)
}
// If we have a matching account we are authorized
_, ok := se.approved[account.Name]
return ok
return a.checkAuth(&se.exportAuth, account, imClaim, nil)
}
// ok if we are here we did not match directly so we need to test each one.
// The import subject arg has to take precedence, meaning the export
Expand All @@ -2391,15 +2425,10 @@ func (a *Account) checkServiceExportApproved(account *Account, subject string, i
tokens := strings.Split(subject, tsep)
for subj, se := range a.exports.services {
if isSubsetMatch(tokens, subj) {
if se == nil || (se.approved == nil && !se.tokenReq) {
if se == nil {
return true
}
// Check if token required
if se.tokenReq {
return a.checkActivation(account, imClaim, true)
}
_, ok := se.approved[account.Name]
return ok
return a.checkAuth(&se.exportAuth, account, imClaim, tokens)
}
}
return false
Expand Down Expand Up @@ -2868,7 +2897,8 @@ func (s *Server) updateAccountClaimsWithRefresh(a *Account, ac *jwt.AccountClaim
switch e.Type {
case jwt.Stream:
s.Debugf("Adding stream export %q for %s", e.Subject, a.Name)
if err := a.AddStreamExport(string(e.Subject), authAccounts(e.TokenReq)); err != nil {
if err := a.addStreamExportWithAccountPos(
string(e.Subject), authAccounts(e.TokenReq), e.AccountTokenPosition); err != nil {
s.Debugf("Error adding stream export to account [%s]: %v", a.Name, err.Error())
}
case jwt.Service:
Expand All @@ -2880,7 +2910,8 @@ func (s *Server) updateAccountClaimsWithRefresh(a *Account, ac *jwt.AccountClaim
case jwt.ResponseTypeChunked:
rt = Chunked
}
if err := a.AddServiceExportWithResponse(string(e.Subject), rt, authAccounts(e.TokenReq)); err != nil {
if err := a.addServiceExportWithResponseAndAccountPos(
string(e.Subject), rt, authAccounts(e.TokenReq), e.AccountTokenPosition); err != nil {
s.Debugf("Error adding service export to account [%s]: %v", a.Name, err)
continue
}
Expand Down
25 changes: 25 additions & 0 deletions server/accounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,31 @@ func TestImportAuthorized(t *testing.T) {
checkBool(foo.checkStreamImportAuthorized(bar, "*.*", nil), true, t)
checkBool(foo.checkStreamImportAuthorized(bar, "*.>", nil), true, t)

_, foo, bar = simpleAccountServer(t)
foo.addStreamExportWithAccountPos("foo.*", []*Account{}, 2)
foo.addStreamExportWithAccountPos("bar.*.foo", []*Account{}, 2)
if err := foo.addStreamExportWithAccountPos("baz.*.>", []*Account{}, 3); err == nil {
t.Fatal("expected error")
}
checkBool(foo.checkStreamImportAuthorized(bar, fmt.Sprintf("foo.%s", bar.Name), nil), true, t)
checkBool(foo.checkStreamImportAuthorized(bar, fmt.Sprintf("bar.%s.foo", bar.Name), nil), true, t)
checkBool(foo.checkStreamImportAuthorized(bar, fmt.Sprintf("baz.foo.%s", bar.Name), nil), false, t)
checkBool(foo.checkStreamImportAuthorized(bar, "foo.X", nil), false, t)
checkBool(foo.checkStreamImportAuthorized(bar, "bar.X.foo", nil), false, t)
checkBool(foo.checkStreamImportAuthorized(bar, "baz.foo.X", nil), false, t)

foo.addServiceExportWithAccountPos("a.*", []*Account{}, 2)
foo.addServiceExportWithAccountPos("b.*.a", []*Account{}, 2)
if err := foo.addServiceExportWithAccountPos("c.*.>", []*Account{}, 3); err == nil {
t.Fatal("expected error")
}
checkBool(foo.checkServiceImportAuthorized(bar, fmt.Sprintf("a.%s", bar.Name), nil), true, t)
checkBool(foo.checkServiceImportAuthorized(bar, fmt.Sprintf("b.%s.a", bar.Name), nil), true, t)
checkBool(foo.checkServiceImportAuthorized(bar, fmt.Sprintf("c.a.%s", bar.Name), nil), false, t)
checkBool(foo.checkServiceImportAuthorized(bar, "a.X", nil), false, t)
checkBool(foo.checkServiceImportAuthorized(bar, "b.X.a", nil), false, t)
checkBool(foo.checkServiceImportAuthorized(bar, "c.a.X", nil), false, t)

// Reset and test pwc and fwc
s, foo, bar := simpleAccountServer(t)
foo.AddStreamExport("foo.*.baz.>", []*Account{bar})
Expand Down