Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Prepare in the same place as the normal rego policy
  • Loading branch information
Emyrk committed Feb 9, 2023
commit 46f9a84168ca487f9bb59a53775a3b7faea7f4bf
30 changes: 24 additions & 6 deletions coderd/rbac/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ func Filter[O Objecter](ctx context.Context, auth Authorizer, subject Subject, a

// RegoAuthorizer will use a prepared rego query for performing authorize()
type RegoAuthorizer struct {
query rego.PreparedEvalQuery
query rego.PreparedEvalQuery
partialQuery rego.PreparedPartialQuery

authorizeHist *prometheus.HistogramVec
prepareHist prometheus.Histogram
Expand All @@ -151,9 +152,10 @@ var (
// Load the policy from policy.rego in this directory.
//
//go:embed policy.rego
policy string
queryOnce sync.Once
query rego.PreparedEvalQuery
policy string
queryOnce sync.Once
query rego.PreparedEvalQuery
partialQuery rego.PreparedPartialQuery
)

func NewAuthorizer(registry prometheus.Registerer) *RegoAuthorizer {
Expand All @@ -166,6 +168,21 @@ func NewAuthorizer(registry prometheus.Registerer) *RegoAuthorizer {
if err != nil {
panic(xerrors.Errorf("compile rego: %w", err))
}

partialQuery, err = rego.New(
Copy link
Member

Choose a reason for hiding this comment

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

review: this is inside queryOnce.Do()

rego.Unknowns([]string{
"input.object.id",
"input.object.owner",
"input.object.org_owner",
"input.object.acl_user_list",
"input.object.acl_group_list",
}),
rego.Query("data.authz.allow = true"),
rego.Module("policy.rego", policy),
).PrepareForPartial(context.Background())
Copy link
Member

Choose a reason for hiding this comment

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

review: I wondered whether it would make sense to pass through a higher-level context here (e.g. NewAuthorizer(ctx, registry)) but the place we call NewAuthorizer doesn't appear to have any application-level context already available.

if err != nil {
panic(xerrors.Errorf("compile partial rego: %w", err))
}
})

// Register metrics to prometheus.
Expand Down Expand Up @@ -207,7 +224,8 @@ func NewAuthorizer(registry prometheus.Registerer) *RegoAuthorizer {
})

return &RegoAuthorizer{
query: query,
query: query,
partialQuery: partialQuery,

authorizeHist: authorizeHistogram,
prepareHist: prepareHistogram,
Expand Down Expand Up @@ -289,7 +307,7 @@ func (a RegoAuthorizer) Prepare(ctx context.Context, subject Subject, action Act
)
defer span.End()

prepared, err := newPartialAuthorizer(ctx, subject, action, objectType)
prepared, err := a.newPartialAuthorizer(ctx, subject, action, objectType)
if err != nil {
return nil, xerrors.Errorf("new partial authorizer: %w", err)
}
Expand Down
29 changes: 3 additions & 26 deletions coderd/rbac/partial.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,24 +127,7 @@ EachQueryLoop:
pa.subjectInput, pa.subjectAction, pa.subjectResourceType, nil)
}

// Precompiled values to be reused for each Prepare call.
// These values are static and do not change.
var (
// unknownTerms are the unknown values in the rego input.
// These values are pre-parsed to prevent reparsing on every Prepare call.
unknownTerms = []*ast.Term{
ast.MustParseTerm("input.object.id"),
ast.MustParseTerm("input.object.owner"),
ast.MustParseTerm("input.object.org_owner"),
ast.MustParseTerm("input.object.acl_user_list"),
ast.MustParseTerm("input.object.acl_group_list"),
}

partialQuery = ast.MustParseBody("data.authz.allow = true")
policyModule = ast.MustParseModule(policy)
)

func newPartialAuthorizer(ctx context.Context, subject Subject, action Action, objectType string) (*PartialAuthorizer, error) {
func (a RegoAuthorizer) newPartialAuthorizer(ctx context.Context, subject Subject, action Action, objectType string) (*PartialAuthorizer, error) {
if subject.Roles == nil {
return nil, xerrors.Errorf("subject must have roles")
}
Expand All @@ -157,14 +140,8 @@ func newPartialAuthorizer(ctx context.Context, subject Subject, action Action, o
return nil, xerrors.Errorf("prepare input: %w", err)
}

// Run the rego policy with a few unknown fields. This should simplify our
// policy to a set of queries.
partialQueries, err := rego.New(
rego.ParsedQuery(partialQuery),
rego.ParsedModule(policyModule),
rego.ParsedUnknowns(unknownTerms),
rego.ParsedInput(input),
).Partial(ctx)
partialQueries, err := a.partialQuery.Partial(ctx, rego.EvalParsedInput(input))

if err != nil {
return nil, xerrors.Errorf("prepare: %w", err)
}
Expand Down