authorize: add support for cidr lookups (#3277)

This commit is contained in:
Caleb Doxsey 2022-04-19 22:18:34 +00:00 committed by GitHub
parent 9dbe12fe99
commit c19048649a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 372 additions and 89 deletions

View file

@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/pomerium/pomerium/authorize/evaluator" "github.com/pomerium/pomerium/authorize/evaluator"
"github.com/pomerium/pomerium/authorize/internal/store"
"github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/telemetry/metrics" "github.com/pomerium/pomerium/internal/telemetry/metrics"
@ -21,7 +22,7 @@ import (
// Authorize struct holds // Authorize struct holds
type Authorize struct { type Authorize struct {
state *atomicAuthorizeState state *atomicAuthorizeState
store *evaluator.Store store *store.Store
currentOptions *config.AtomicOptions currentOptions *config.AtomicOptions
accessTracker *AccessTracker accessTracker *AccessTracker
@ -37,7 +38,7 @@ type Authorize struct {
func New(cfg *config.Config) (*Authorize, error) { func New(cfg *config.Config) (*Authorize, error) {
a := &Authorize{ a := &Authorize{
currentOptions: config.NewAtomicOptions(), currentOptions: config.NewAtomicOptions(),
store: evaluator.NewStore(), store: store.New(),
dataBrokerInitialSync: make(chan struct{}), dataBrokerInitialSync: make(chan struct{}),
} }
a.accessTracker = NewAccessTracker(a, accessTrackerMaxSize, accessTrackerDebouncePeriod) a.accessTracker = NewAccessTracker(a, accessTrackerMaxSize, accessTrackerDebouncePeriod)
@ -85,7 +86,7 @@ func validateOptions(o *config.Options) error {
} }
// newPolicyEvaluator returns an policy evaluator. // newPolicyEvaluator returns an policy evaluator.
func newPolicyEvaluator(opts *config.Options, store *evaluator.Store) (*evaluator.Evaluator, error) { func newPolicyEvaluator(opts *config.Options, store *store.Store) (*evaluator.Evaluator, error) {
metrics.AddPolicyCountCallback("pomerium-authorize", func() int64 { metrics.AddPolicyCountCallback("pomerium-authorize", func() int64 {
return int64(len(opts.GetAllPolicies())) return int64(len(opts.GetAllPolicies()))
}) })

View file

@ -16,6 +16,7 @@ import (
"google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/encoding/protojson"
"github.com/pomerium/pomerium/authorize/evaluator" "github.com/pomerium/pomerium/authorize/evaluator"
"github.com/pomerium/pomerium/authorize/internal/store"
"github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/encoding/jws" "github.com/pomerium/pomerium/internal/encoding/jws"
"github.com/pomerium/pomerium/internal/testutil" "github.com/pomerium/pomerium/internal/testutil"
@ -39,7 +40,7 @@ func TestAuthorize_okResponse(t *testing.T) {
encoder, _ := jws.NewHS256Signer([]byte{0, 0, 0, 0}) encoder, _ := jws.NewHS256Signer([]byte{0, 0, 0, 0})
a.state.Load().encoder = encoder a.state.Load().encoder = encoder
a.currentOptions.Store(opt) a.currentOptions.Store(opt)
a.store = evaluator.NewStoreFromProtos(0, a.store = store.NewFromProtos(0,
&session.Session{ &session.Session{
Id: "SESSION_ID", Id: "SESSION_ID",
UserId: "USER_ID", UserId: "USER_ID",

View file

@ -11,6 +11,7 @@ import (
"github.com/go-jose/go-jose/v3" "github.com/go-jose/go-jose/v3"
"github.com/open-policy-agent/opa/rego" "github.com/open-policy-agent/opa/rego"
"github.com/pomerium/pomerium/authorize/internal/store"
"github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/log"
@ -77,14 +78,14 @@ type Result struct {
// An Evaluator evaluates policies. // An Evaluator evaluates policies.
type Evaluator struct { type Evaluator struct {
store *Store store *store.Store
policyEvaluators map[uint64]*PolicyEvaluator policyEvaluators map[uint64]*PolicyEvaluator
headersEvaluators *HeadersEvaluator headersEvaluators *HeadersEvaluator
clientCA []byte clientCA []byte
} }
// New creates a new Evaluator. // New creates a new Evaluator.
func New(ctx context.Context, store *Store, options ...Option) (*Evaluator, error) { func New(ctx context.Context, store *store.Store, options ...Option) (*Evaluator, error) {
e := &Evaluator{store: store} e := &Evaluator{store: store}
cfg := getConfig(options...) cfg := getConfig(options...)

View file

@ -14,6 +14,7 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
"github.com/pomerium/pomerium/authorize/internal/store"
"github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/pkg/cryptutil" "github.com/pomerium/pomerium/pkg/cryptutil"
@ -35,7 +36,7 @@ func TestEvaluator(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
eval := func(t *testing.T, options []Option, data []proto.Message, req *Request) (*Result, error) { eval := func(t *testing.T, options []Option, data []proto.Message, req *Request) (*Result, error) {
store := NewStoreFromProtos(math.MaxUint64, data...) store := store.NewFromProtos(math.MaxUint64, data...)
store.UpdateIssuer("authenticate.example.com") store.UpdateIssuer("authenticate.example.com")
store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY")) store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY"))
store.UpdateSigningKey(privateJWK) store.UpdateSigningKey(privateJWK)
@ -512,7 +513,7 @@ func mustParseURL(str string) *url.URL {
} }
func BenchmarkEvaluator_Evaluate(b *testing.B) { func BenchmarkEvaluator_Evaluate(b *testing.B) {
store := NewStore() store := store.New()
policies := []config.Policy{ policies := []config.Policy{
{ {

View file

@ -9,6 +9,7 @@ import (
"github.com/open-policy-agent/opa/rego" "github.com/open-policy-agent/opa/rego"
"github.com/pomerium/pomerium/authorize/evaluator/opa" "github.com/pomerium/pomerium/authorize/evaluator/opa"
"github.com/pomerium/pomerium/authorize/internal/store"
"github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/telemetry/trace" "github.com/pomerium/pomerium/internal/telemetry/trace"
"github.com/pomerium/pomerium/internal/urlutil" "github.com/pomerium/pomerium/internal/urlutil"
@ -56,7 +57,7 @@ type HeadersEvaluator struct {
} }
// NewHeadersEvaluator creates a new HeadersEvaluator. // NewHeadersEvaluator creates a new HeadersEvaluator.
func NewHeadersEvaluator(ctx context.Context, store *Store) (*HeadersEvaluator, error) { func NewHeadersEvaluator(ctx context.Context, store *store.Store) (*HeadersEvaluator, error) {
r := rego.New( r := rego.New(
rego.Store(store), rego.Store(store),
rego.Module("pomerium.headers", opa.HeadersRego), rego.Module("pomerium.headers", opa.HeadersRego),

View file

@ -12,6 +12,7 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/structpb"
"github.com/pomerium/pomerium/authorize/internal/store"
"github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/pkg/cryptutil" "github.com/pomerium/pomerium/pkg/cryptutil"
"github.com/pomerium/pomerium/pkg/grpc/directory" "github.com/pomerium/pomerium/pkg/grpc/directory"
@ -50,7 +51,7 @@ func TestHeadersEvaluator(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
eval := func(t *testing.T, data []proto.Message, input *HeadersRequest) (*HeadersResponse, error) { eval := func(t *testing.T, data []proto.Message, input *HeadersRequest) (*HeadersResponse, error) {
store := NewStoreFromProtos(math.MaxUint64, data...) store := store.NewFromProtos(math.MaxUint64, data...)
store.UpdateIssuer("authenticate.example.com") store.UpdateIssuer("authenticate.example.com")
store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY")) store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY"))
store.UpdateSigningKey(privateJWK) store.UpdateSigningKey(privateJWK)

View file

@ -8,6 +8,7 @@ import (
"github.com/open-policy-agent/opa/rego" "github.com/open-policy-agent/opa/rego"
octrace "go.opencensus.io/trace" octrace "go.opencensus.io/trace"
"github.com/pomerium/pomerium/authorize/internal/store"
"github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/telemetry/trace" "github.com/pomerium/pomerium/internal/telemetry/trace"
@ -97,7 +98,7 @@ type PolicyEvaluator struct {
} }
// NewPolicyEvaluator creates a new PolicyEvaluator. // NewPolicyEvaluator creates a new PolicyEvaluator.
func NewPolicyEvaluator(ctx context.Context, store *Store, configPolicy *config.Policy) (*PolicyEvaluator, error) { func NewPolicyEvaluator(ctx context.Context, store *store.Store, configPolicy *config.Policy) (*PolicyEvaluator, error) {
e := new(PolicyEvaluator) e := new(PolicyEvaluator)
// generate the base rego script for the policy // generate the base rego script for the policy

View file

@ -9,7 +9,9 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
"github.com/pomerium/pomerium/authorize/internal/store"
"github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/pkg/cryptutil" "github.com/pomerium/pomerium/pkg/cryptutil"
"github.com/pomerium/pomerium/pkg/grpc/session" "github.com/pomerium/pomerium/pkg/grpc/session"
@ -27,7 +29,7 @@ func TestPolicyEvaluator(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
eval := func(t *testing.T, policy *config.Policy, data []proto.Message, input *PolicyRequest) (*PolicyResponse, error) { eval := func(t *testing.T, policy *config.Policy, data []proto.Message, input *PolicyRequest) (*PolicyResponse, error) {
store := NewStoreFromProtos(math.MaxUint64, data...) store := store.NewFromProtos(math.MaxUint64, data...)
store.UpdateIssuer("authenticate.example.com") store.UpdateIssuer("authenticate.example.com")
store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY")) store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY"))
store.UpdateSigningKey(privateJWK) store.UpdateSigningKey(privateJWK)
@ -196,4 +198,41 @@ func TestPolicyEvaluator(t *testing.T) {
}, output) }, output)
}) })
}) })
t.Run("cidr", func(t *testing.T) {
r1 := &structpb.Struct{Fields: map[string]*structpb.Value{
"$index": structpb.NewStructValue(&structpb.Struct{Fields: map[string]*structpb.Value{
"cidr": structpb.NewStringValue("192.168.0.0/16"),
}}),
"country": structpb.NewStringValue("US"),
}}
p := &config.Policy{
From: "https://from.example.com",
To: config.WeightedURLs{{URL: *mustParseURL("https://to.example.com")}},
SubPolicies: []config.SubPolicy{
{Rego: []string{`
package pomerium.policy
allow {
record := get_databroker_record("type.googleapis.com/google.protobuf.Struct", "192.168.0.7")
record.country == "US"
}
`}},
},
}
output, err := eval(t,
p,
[]proto.Message{s1, u1, s2, u2, r1},
&PolicyRequest{
HTTP: RequestHTTP{Method: "GET", URL: "https://from.example.com/path"},
Session: RequestSession{ID: "s1"},
IsValidClientCertificate: true,
})
require.NoError(t, err)
assert.Equal(t, &PolicyResponse{
Allow: NewRuleResult(true),
Deny: NewRuleResult(false, criteria.ReasonValidClientCertificateOrNoneRequired),
}, output)
})
} }

View file

@ -0,0 +1,196 @@
package store
import (
"sync"
"github.com/kentik/patricia"
"github.com/kentik/patricia/string_tree"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
)
const (
indexField = "$index"
cidrField = "cidr"
)
type index struct {
mu sync.RWMutex
byType map[string]*recordIndex
}
func newIndex() *index {
idx := new(index)
idx.clear()
return idx
}
func (idx *index) clear() {
idx.mu.Lock()
defer idx.mu.Unlock()
idx.byType = map[string]*recordIndex{}
}
func (idx *index) delete(typeURL, id string) {
idx.mu.Lock()
defer idx.mu.Unlock()
ridx, ok := idx.byType[typeURL]
if !ok {
return
}
ridx.delete(id)
if len(ridx.byID) == 0 {
delete(idx.byType, typeURL)
}
}
func (idx *index) find(typeURL, id string) proto.Message {
idx.mu.RLock()
defer idx.mu.RUnlock()
ridx, ok := idx.byType[typeURL]
if !ok {
return nil
}
return ridx.find(id)
}
func (idx *index) get(typeURL, id string) proto.Message {
idx.mu.RLock()
defer idx.mu.RUnlock()
ridx, ok := idx.byType[typeURL]
if !ok {
return nil
}
return ridx.get(id)
}
func (idx *index) set(typeURL, id string, msg proto.Message) {
idx.mu.Lock()
defer idx.mu.Unlock()
ridx, ok := idx.byType[typeURL]
if !ok {
ridx = newRecordIndex()
idx.byType[typeURL] = ridx
}
ridx.set(id, msg)
}
// a recordIndex indexes records for of a specific type
type recordIndex struct {
byID map[string]proto.Message
byCIDRV4 *string_tree.TreeV4
byCIDRV6 *string_tree.TreeV6
}
// newRecordIndex creates a new record index.
func newRecordIndex() *recordIndex {
return &recordIndex{
byID: map[string]proto.Message{},
byCIDRV4: string_tree.NewTreeV4(),
byCIDRV6: string_tree.NewTreeV6(),
}
}
func (idx *recordIndex) delete(id string) {
r, ok := idx.byID[id]
if !ok {
return
}
delete(idx.byID, id)
addr4, addr6 := getIndexCIDR(r)
if addr4 != nil {
idx.byCIDRV4.Delete(*addr4, func(payload, val string) bool {
return payload == val
}, id)
}
if addr6 != nil {
idx.byCIDRV6.Delete(*addr6, func(payload, val string) bool {
return payload == val
}, id)
}
}
func (idx *recordIndex) find(idOrString string) proto.Message {
r, ok := idx.byID[idOrString]
if ok {
return r
}
addrv4, addrv6, _ := patricia.ParseIPFromString(idOrString)
if addrv4 != nil {
found, id := idx.byCIDRV4.FindDeepestTag(*addrv4)
if found {
return idx.byID[id]
}
}
if addrv6 != nil {
found, id := idx.byCIDRV6.FindDeepestTag(*addrv6)
if found {
return idx.byID[id]
}
}
return nil
}
func (idx *recordIndex) get(id string) proto.Message {
return idx.byID[id]
}
func (idx *recordIndex) set(id string, msg proto.Message) {
_, ok := idx.byID[id]
if ok {
idx.delete(id)
}
idx.byID[id] = msg
addr4, addr6 := getIndexCIDR(msg)
if addr4 != nil {
idx.byCIDRV4.Set(*addr4, id)
}
if addr6 != nil {
idx.byCIDRV6.Set(*addr6, id)
}
}
func getIndexCIDR(msg proto.Message) (*patricia.IPv4Address, *patricia.IPv6Address) {
var s *structpb.Struct
if sv, ok := msg.(*structpb.Value); ok {
s = sv.GetStructValue()
} else {
s, _ = msg.(*structpb.Struct)
}
if s == nil {
return nil, nil
}
f, ok := s.Fields[indexField]
if !ok {
return nil, nil
}
obj := f.GetStructValue()
if obj == nil {
return nil, nil
}
cf, ok := obj.Fields[cidrField]
if !ok {
return nil, nil
}
c := cf.GetStringValue()
if c == "" {
return nil, nil
}
addr4, addr6, _ := patricia.ParseIPFromString(c)
return addr4, addr6
}

View file

@ -0,0 +1,74 @@
package store
import (
"testing"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/types/known/structpb"
)
func TestByID(t *testing.T) {
idx := newIndex()
r1 := &structpb.Struct{Fields: map[string]*structpb.Value{
"id": structpb.NewStringValue("r1"),
}}
idx.set("example.com/record", "r1", r1)
assert.Equal(t, r1, idx.get("example.com/record", "r1"))
idx.delete("example.com/record", "r1")
assert.Nil(t, idx.get("example.com/record", "r1"))
}
func TestByCIDR(t *testing.T) {
t.Run("ipv4", func(t *testing.T) {
idx := newIndex()
r1 := &structpb.Struct{Fields: map[string]*structpb.Value{
"$index": structpb.NewStructValue(&structpb.Struct{Fields: map[string]*structpb.Value{
"cidr": structpb.NewStringValue("192.168.0.0/16"),
}}),
"id": structpb.NewStringValue("r1"),
}}
idx.set("example.com/record", "r1", r1)
r2 := &structpb.Struct{Fields: map[string]*structpb.Value{
"$index": structpb.NewStructValue(&structpb.Struct{Fields: map[string]*structpb.Value{
"cidr": structpb.NewStringValue("192.168.0.0/24"),
}}),
"id": structpb.NewStringValue("r2"),
}}
idx.set("example.com/record", "r2", r2)
assert.Equal(t, r2, idx.find("example.com/record", "192.168.0.7"))
idx.delete("example.com/record", "r2")
assert.Equal(t, r1, idx.find("example.com/record", "192.168.0.7"))
idx.delete("example.com/record", "r1")
assert.Nil(t, idx.find("example.com/record", "192.168.0.7"))
})
t.Run("ipv6", func(t *testing.T) {
idx := newIndex()
r1 := &structpb.Struct{Fields: map[string]*structpb.Value{
"$index": structpb.NewStructValue(&structpb.Struct{Fields: map[string]*structpb.Value{
"cidr": structpb.NewStringValue("2001:db8::/32"),
}}),
"id": structpb.NewStringValue("r1"),
}}
idx.set("example.com/record", "r1", r1)
r2 := &structpb.Struct{Fields: map[string]*structpb.Value{
"$index": structpb.NewStructValue(&structpb.Struct{Fields: map[string]*structpb.Value{
"cidr": structpb.NewStringValue("2001:db8::/48"),
}}),
"id": structpb.NewStringValue("r2"),
}}
idx.set("example.com/record", "r2", r2)
assert.Equal(t, r2, idx.find("example.com/record", "2001:db8::"))
idx.delete("example.com/record", "r2")
assert.Equal(t, r1, idx.find("example.com/record", "2001:db8::"))
idx.delete("example.com/record", "r1")
assert.Nil(t, idx.find("example.com/record", "2001:db8::"))
})
}

View file

@ -1,10 +1,10 @@
package evaluator // Package store contains a datastore for authorization policy evaluation.
package store
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"sync"
"sync/atomic" "sync/atomic"
"github.com/go-jose/go-jose/v3" "github.com/go-jose/go-jose/v3"
@ -24,83 +24,25 @@ import (
"github.com/pomerium/pomerium/pkg/protoutil" "github.com/pomerium/pomerium/pkg/protoutil"
) )
type dataBrokerData struct {
mu sync.RWMutex
m map[string]map[string]proto.Message
}
func newDataBrokerData() *dataBrokerData {
return &dataBrokerData{
m: map[string]map[string]proto.Message{},
}
}
func (dbd *dataBrokerData) clear() {
dbd.mu.Lock()
defer dbd.mu.Unlock()
dbd.m = map[string]map[string]proto.Message{}
}
func (dbd *dataBrokerData) delete(typeURL, id string) {
dbd.mu.Lock()
defer dbd.mu.Unlock()
m, ok := dbd.m[typeURL]
if !ok {
return
}
delete(m, id)
if len(m) == 0 {
delete(dbd.m, typeURL)
}
}
func (dbd *dataBrokerData) get(typeURL, id string) proto.Message {
dbd.mu.RLock()
defer dbd.mu.RUnlock()
m, ok := dbd.m[typeURL]
if !ok {
return nil
}
return m[id]
}
func (dbd *dataBrokerData) set(typeURL, id string, msg proto.Message) {
dbd.mu.Lock()
defer dbd.mu.Unlock()
m, ok := dbd.m[typeURL]
if !ok {
m = map[string]proto.Message{}
dbd.m[typeURL] = m
}
m[id] = msg
}
// A Store stores data for the OPA rego policy evaluation. // A Store stores data for the OPA rego policy evaluation.
type Store struct { type Store struct {
storage.Store storage.Store
index *index
dataBrokerData *dataBrokerData
dataBrokerServerVersion, dataBrokerRecordVersion uint64 dataBrokerServerVersion, dataBrokerRecordVersion uint64
} }
// NewStore creates a new Store. // New creates a new Store.
func NewStore() *Store { func New() *Store {
return &Store{ return &Store{
Store: inmem.New(), Store: inmem.New(),
dataBrokerData: newDataBrokerData(), index: newIndex(),
} }
} }
// NewStoreFromProtos creates a new Store from an existing set of protobuf messages. // NewFromProtos creates a new Store from an existing set of protobuf messages.
func NewStoreFromProtos(serverVersion uint64, msgs ...proto.Message) *Store { func NewFromProtos(serverVersion uint64, msgs ...proto.Message) *Store {
s := NewStore() s := New()
for _, msg := range msgs { for _, msg := range msgs {
any := protoutil.NewAny(msg) any := protoutil.NewAny(msg)
record := new(databroker.Record) record := new(databroker.Record)
@ -120,7 +62,7 @@ func NewStoreFromProtos(serverVersion uint64, msgs ...proto.Message) *Store {
// ClearRecords removes all the records from the store. // ClearRecords removes all the records from the store.
func (s *Store) ClearRecords() { func (s *Store) ClearRecords() {
s.dataBrokerData.clear() s.index.clear()
} }
// GetDataBrokerVersions gets the databroker versions. // GetDataBrokerVersions gets the databroker versions.
@ -131,8 +73,8 @@ func (s *Store) GetDataBrokerVersions() (serverVersion, recordVersion uint64) {
// GetRecordData gets a record's data from the store. `nil` is returned // GetRecordData gets a record's data from the store. `nil` is returned
// if no record exists for the given type and id. // if no record exists for the given type and id.
func (s *Store) GetRecordData(typeURL, id string) proto.Message { func (s *Store) GetRecordData(typeURL, idOrValue string) proto.Message {
return s.dataBrokerData.get(typeURL, id) return s.index.find(typeURL, idOrValue)
} }
// UpdateIssuer updates the issuer in the store. The issuer is used as part of JWT construction. // UpdateIssuer updates the issuer in the store. The issuer is used as part of JWT construction.
@ -159,10 +101,10 @@ func (s *Store) UpdateRoutePolicies(routePolicies []config.Policy) {
// UpdateRecord updates a record in the store. // UpdateRecord updates a record in the store.
func (s *Store) UpdateRecord(serverVersion uint64, record *databroker.Record) { func (s *Store) UpdateRecord(serverVersion uint64, record *databroker.Record) {
if record.GetDeletedAt() != nil { if record.GetDeletedAt() != nil {
s.dataBrokerData.delete(record.GetType(), record.GetId()) s.index.delete(record.GetType(), record.GetId())
} else { } else {
msg, _ := record.GetData().UnmarshalNew() msg, _ := record.GetData().UnmarshalNew()
s.dataBrokerData.set(record.GetType(), record.GetId(), msg) s.index.set(record.GetType(), record.GetId(), msg)
} }
s.write("/databroker_server_version", fmt.Sprint(serverVersion)) s.write("/databroker_server_version", fmt.Sprint(serverVersion))
s.write("/databroker_record_version", fmt.Sprint(record.GetVersion())) s.write("/databroker_record_version", fmt.Sprint(record.GetVersion()))

View file

@ -1,9 +1,10 @@
package evaluator package store
import ( import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
"github.com/pomerium/pomerium/pkg/grpc/databroker" "github.com/pomerium/pomerium/pkg/grpc/databroker"
@ -12,8 +13,8 @@ import (
) )
func TestStore(t *testing.T) { func TestStore(t *testing.T) {
s := NewStore()
t.Run("records", func(t *testing.T) { t.Run("records", func(t *testing.T) {
s := New()
u := &user.User{ u := &user.User{
Version: "v1", Version: "v1",
Id: "u1", Id: "u1",
@ -61,4 +62,22 @@ func TestStore(t *testing.T) {
v = s.GetRecordData(any.GetTypeUrl(), u.GetId()) v = s.GetRecordData(any.GetTypeUrl(), u.GetId())
assert.Nil(t, v) assert.Nil(t, v)
}) })
t.Run("cidr", func(t *testing.T) {
s := New()
any := protoutil.NewAny(&structpb.Struct{Fields: map[string]*structpb.Value{
"$index": structpb.NewStructValue(&structpb.Struct{Fields: map[string]*structpb.Value{
"cidr": structpb.NewStringValue("192.168.0.0/16"),
}}),
"id": structpb.NewStringValue("r1"),
}})
s.UpdateRecord(0, &databroker.Record{
Version: 1,
Type: any.GetTypeUrl(),
Id: "r1",
Data: any,
})
v := s.GetRecordData(any.GetTypeUrl(), "192.168.0.7")
assert.NotNil(t, v)
})
} }

View file

@ -8,6 +8,7 @@ import (
googlegrpc "google.golang.org/grpc" googlegrpc "google.golang.org/grpc"
"github.com/pomerium/pomerium/authorize/evaluator" "github.com/pomerium/pomerium/authorize/evaluator"
"github.com/pomerium/pomerium/authorize/internal/store"
"github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/encoding" "github.com/pomerium/pomerium/internal/encoding"
"github.com/pomerium/pomerium/internal/encoding/jws" "github.com/pomerium/pomerium/internal/encoding/jws"
@ -27,7 +28,7 @@ type authorizeState struct {
auditEncryptor *protoutil.Encryptor auditEncryptor *protoutil.Encryptor
} }
func newAuthorizeStateFromConfig(cfg *config.Config, store *evaluator.Store) (*authorizeState, error) { func newAuthorizeStateFromConfig(cfg *config.Config, store *store.Store) (*authorizeState, error) {
if err := validateOptions(cfg.Options); err != nil { if err := validateOptions(cfg.Options); err != nil {
return nil, fmt.Errorf("authorize: bad options: %w", err) return nil, fmt.Errorf("authorize: bad options: %w", err)
} }

5
go.mod
View file

@ -75,7 +75,10 @@ require (
sigs.k8s.io/yaml v1.3.0 sigs.k8s.io/yaml v1.3.0
) )
require github.com/CAFxX/httpcompression v0.0.8 require (
github.com/CAFxX/httpcompression v0.0.8
github.com/kentik/patricia v1.0.0
)
require ( require (
4d63.com/gochecknoglobals v0.1.0 // indirect 4d63.com/gochecknoglobals v0.1.0 // indirect

2
go.sum
View file

@ -855,6 +855,8 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY=
github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/kentik/patricia v1.0.0 h1:jx/8kXf0JvQEHNPX4njL+PDzpxxqNKg0RjA8hJcX38A=
github.com/kentik/patricia v1.0.0/go.mod h1:e0nkPLU9NQl8v05ukfHU6+R5ykbKcXO+NqaC3ifTm0Y=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=