Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
all: Add unary hook that preloads rights in the context
- Loading branch information
1 parent
0e88969
commit 60ab25e
Showing
10 changed files
with
328 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 2 additions & 2 deletions
4
pkg/rpcclient/identityserver/cache_noop.go → pkg/auth/rights/cache_noop.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
// Copyright © 2018 The Things Network Foundation, distributed under the MIT license (see LICENSE file) | ||
|
||
package identityserver | ||
package rights | ||
|
||
import "github.com/TheThingsNetwork/ttn/pkg/ttnpb" | ||
|
||
// noopCache implements cache and caches nothing. | ||
type noopCache struct{} | ||
|
||
func (c *noopCache) GetOrFetch(auth, entityID string, fetch func() ([]ttnpb.Right, error)) ([]ttnpb.Right, error) { | ||
func (c *noopCache) GetOrFetch(key string, fetch func() ([]ttnpb.Right, error)) ([]ttnpb.Right, error) { | ||
return fetch() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Copyright © 2018 The Things Network Foundation, distributed under the MIT license (see LICENSE file) | ||
|
||
package rights | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
|
||
"github.com/TheThingsNetwork/ttn/pkg/log" | ||
"github.com/TheThingsNetwork/ttn/pkg/ttnpb" | ||
"github.com/TheThingsNetwork/ttn/pkg/util/test" | ||
"github.com/smartystreets/assertions" | ||
"github.com/smartystreets/assertions/should" | ||
) | ||
|
||
// Make sure in compile time that noopCache and ttlCache implements cache. | ||
var ( | ||
_ cache = new(noopCache) | ||
_ cache = new(ttlCache) | ||
) | ||
|
||
func TestCacheTTL(t *testing.T) { | ||
a := assertions.New(t) | ||
ctx, cancel := context.WithCancel(log.NewContextWithField(log.NewContext(context.Background(), test.GetLogger(t)), "hook", "rights")) | ||
cache := newTTLCache(ctx, time.Duration(time.Millisecond*200)) | ||
|
||
key := "foo" | ||
|
||
rights, err := cache.GetOrFetch(key, func() ([]ttnpb.Right, error) { | ||
return []ttnpb.Right{ttnpb.Right(1)}, nil | ||
}) | ||
a.So(err, should.BeNil) | ||
a.So(rights, should.Resemble, []ttnpb.Right{ttnpb.Right(1)}) | ||
a.So(cache.entries, should.HaveLength, 1) | ||
|
||
// Although fetch function is different the previous response is still cached. | ||
rights, err = cache.GetOrFetch(key, func() ([]ttnpb.Right, error) { | ||
return []ttnpb.Right{ttnpb.Right(2)}, nil | ||
}) | ||
a.So(err, should.BeNil) | ||
a.So(rights, should.Resemble, []ttnpb.Right{ttnpb.Right(1)}) | ||
a.So(cache.entries, should.HaveLength, 1) | ||
|
||
// Sleep for 250 milliseconds so the entry expires. | ||
time.Sleep(time.Millisecond * 250) | ||
|
||
// Entry has been garbage collected. | ||
a.So(cache.entries, should.HaveLength, 0) | ||
|
||
// Stop the garbage collector. | ||
cancel() | ||
|
||
// Fetch again with different response. | ||
rights, err = cache.GetOrFetch(key, func() ([]ttnpb.Right, error) { | ||
return []ttnpb.Right{ttnpb.Right(2)}, nil | ||
}) | ||
a.So(err, should.BeNil) | ||
a.So(rights, should.Resemble, []ttnpb.Right{ttnpb.Right(2)}) | ||
a.So(cache.entries, should.HaveLength, 1) | ||
|
||
// Sleep for 250 milliseconds again. | ||
time.Sleep(time.Millisecond * 250) | ||
|
||
// Check that the entry has not been garbage collected. | ||
a.So(cache.entries, should.HaveLength, 1) | ||
a.So(cache.entries, should.ContainKey, key) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Copyright © 2018 The Things Network Foundation, distributed under the MIT license (see LICENSE file) | ||
|
||
package rights | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/TheThingsNetwork/ttn/pkg/ttnpb" | ||
) | ||
|
||
type rightsKey int | ||
|
||
const key rightsKey = 1 | ||
|
||
// NewContext returns ctx with the given rights within. | ||
func NewContext(ctx context.Context, rights []ttnpb.Right) context.Context { | ||
return context.WithValue(ctx, key, rights) | ||
} | ||
|
||
// FromContext returns the rights from ctx, otherwise empty slice if they are not found. | ||
func FromContext(ctx context.Context) []ttnpb.Right { | ||
if r, ok := ctx.Value(key).([]ttnpb.Right); ok { | ||
return r | ||
} | ||
return make([]ttnpb.Right, 0) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright © 2018 The Things Network Foundation, distributed under the MIT license (see LICENSE file) | ||
|
||
/* | ||
Package rights implements a gRPC Unary hook that preload rights in the context. | ||
In order to preload the rights the following steps are taken: | ||
1. The hook looks if the request message implements one of either interfaces: | ||
type applicationIdentifiersGetters interface { | ||
GetApplicationID() string | ||
} | ||
type gatewayIdentifiersGetters interface { | ||
GetGatewayID() string | ||
} | ||
If the message implements both interfaces only applicationIdentifiers is taken | ||
into account. | ||
2. The hook gets a gRPC connection to an Identity Server through the | ||
IdentityServerConnector interface and then calls either the ListApplicationRights | ||
or ListGatewayRights method of the Identity Server using the authorization | ||
value of the original request. | ||
Optionally the hook can set up a TTL cache whenever the Config.TTL value is | ||
different to its zero value. | ||
3. The resulting rights are put in the context. | ||
Lastly, the way to check the rights in the protected Unary method is a matter of: | ||
import ( | ||
"github.com/TheThingsNetwork/ttn/pkg/auth" | ||
"github.com/TheThingsNetwork/ttn/pkg/auth/rights" | ||
"github.com/TheThingsNetwork/ttn/pkg/ttnpb" | ||
) | ||
func (a *ApplicationServer) MyMethod(ctx context.Context, ids *ttnpb.ApplicationIdentifiers) (*pbtypes.Empty, error) { | ||
if !ttnpb.IncludesRights(rights.FromContext(ctx), ttnpb.RIGHT_APPLICATION_TRAFFIC_READ) { | ||
return nil, auth.ErrNotAuthorized.New(nil) | ||
} | ||
... | ||
.... | ||
} | ||
*/ | ||
package rights |
Oops, something went wrong.