Skip to content

Commit

Permalink
Merge pull request #58 from aserto-dev/cache-query
Browse files Browse the repository at this point in the history
Remove v2 + query for relation types
  • Loading branch information
ronenh authored Oct 29, 2024
2 parents 5736b57 + 0508a05 commit ff93a1d
Show file tree
Hide file tree
Showing 33 changed files with 205 additions and 11,085 deletions.
20 changes: 5 additions & 15 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,24 @@ on:
- release-*
env:
PRE_RELEASE: ${{ github.ref == 'refs/heads/main' && 'development' || '' }}
GO_VERSION: "1.22"
GO_RELEASER_VERSION: "v1.24.0"
GO_LANGCI_LINT_VERSION: "v1.56.2"
GO_VERSION: "1.23"
GO_LANGCI_LINT_VERSION: "v1.61.0"
GO_TESTSUM_VERSION: "1.11.0"

jobs:
test:
runs-on: ubuntu-latest
steps:
-
-
name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
-
-
name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
-
name: Build
uses: goreleaser/goreleaser-action@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
distribution: goreleaser
version: ${{ env.GO_RELEASER_VERSION }}
args: build --clean --snapshot --single-target
-
name: Lint
uses: golangci/golangci-lint-action@v4
Expand All @@ -57,7 +47,7 @@ jobs:
name: Test
run: |
gotestsum --format short-verbose -- -count=1 -v -timeout=240s -coverprofile=cover.out -coverpkg=./... ./...
-
-
name: Upload code coverage
uses: shogo82148/actions-goveralls@v1
continue-on-error: true
Expand Down
25 changes: 8 additions & 17 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ linters-settings:
funlen:
lines: 100
statements: 80
gci:
local-prefixes: github.com/golangci/golangci-lint
goconst:
min-len: 2
min-occurrences: 2
Expand All @@ -40,16 +38,13 @@ linters-settings:
golint:
min-confidence: 0
gomnd:
settings:
mnd:
# don't include the "operation" and "assign"
checks:
- argument
- case
- condition
- return
checks:
- argument
- case
- condition
- return
govet:
check-shadowing: true
shadow: true
settings:
printf:
funcs:
Expand All @@ -75,15 +70,15 @@ linters:
- bodyclose
- dogsled
- errcheck
- exportloopref
- copyloopvar
- exhaustive
- funlen
- gochecknoinits
- goconst
- gocritic
- gocyclo
- godot
- goerr113
- err113
- gofmt
- goimports
- goprintffuncname
Expand Down Expand Up @@ -153,7 +148,3 @@ issues:
- text: "G404"
linters:
- gosec

run:
skip-dirs:
- pkg/testharness/testdata
79 changes: 0 additions & 79 deletions .goreleaser.yml

This file was deleted.

70 changes: 63 additions & 7 deletions cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import (
"github.com/aserto-dev/azm/model/diff"
stts "github.com/aserto-dev/azm/stats"
dsc "github.com/aserto-dev/go-directory/aserto/directory/common/v3"
"github.com/aserto-dev/go-directory/pkg/derr"
"github.com/samber/lo"
)

type (
ObjectName = model.ObjectName
RelationName = model.RelationName
)

type Cache struct {
Expand Down Expand Up @@ -37,7 +44,7 @@ func (c *Cache) CanUpdate(other *model.Model, stats *stts.Stats) error {
}

// ObjectExists, checks if given object type name exists in the model cache.
func (c *Cache) ObjectExists(on model.ObjectName) bool {
func (c *Cache) ObjectExists(on ObjectName) bool {
c.mtx.RLock()
defer c.mtx.RUnlock()

Expand All @@ -46,7 +53,7 @@ func (c *Cache) ObjectExists(on model.ObjectName) bool {
}

// RelationExists, checks if given relation type, for the given object type, exists in the model cache.
func (c *Cache) RelationExists(on model.ObjectName, rn model.RelationName) bool {
func (c *Cache) RelationExists(on ObjectName, rn RelationName) bool {
c.mtx.RLock()
defer c.mtx.RUnlock()

Expand All @@ -58,7 +65,7 @@ func (c *Cache) RelationExists(on model.ObjectName, rn model.RelationName) bool
}

// PermissionExists, checks if given permission, for the given object type, exists in the model cache.
func (c *Cache) PermissionExists(on model.ObjectName, pn model.RelationName) bool {
func (c *Cache) PermissionExists(on ObjectName, pn RelationName) bool {
c.mtx.RLock()
defer c.mtx.RUnlock()

Expand All @@ -80,11 +87,60 @@ func (c *Cache) ValidateRelation(relation *dsc.Relation) error {
defer c.mtx.RUnlock()

return c.model.ValidateRelation(
model.ObjectName(relation.ObjectType),
ObjectName(relation.ObjectType),
model.ObjectID(relation.ObjectId),
model.RelationName(relation.Relation),
model.ObjectName(relation.SubjectType),
RelationName(relation.Relation),
ObjectName(relation.SubjectType),
model.ObjectID(relation.SubjectId),
model.RelationName(relation.SubjectRelation),
RelationName(relation.SubjectRelation),
)
}

// AssignableRelations returns the set of relations that can occur between a given object type
// and a subject type, optionally with a subject relation.
//
// If more than one subject relation is provided, AssignableRelations returns relations that match any
// of the given relations. For example, if the manifest has:
//
// types:
//
// tenant:
// relations:
// admin: group#member
// guest: group#guest
//
// Then AssignableRelations("tenant", "group", "member", "guest") returns ["admin", "guest"].
func (c *Cache) AssignableRelations(on, sn ObjectName, sr ...RelationName) ([]RelationName, error) {
if !c.ObjectExists(on) {
return nil, derr.ErrObjectNotFound.Msg(on.String())
}
if !c.ObjectExists(sn) {
return nil, derr.ErrObjectNotFound.Msg(sn.String())
}
for _, srel := range sr {
if !c.RelationExists(sn, srel) {
return nil, derr.ErrRelationNotFound.Msgf("%s#%s", sn, sr)
}
}

matches := lo.PickBy(c.model.Objects[on].Relations, func(rn RelationName, r *model.Relation) bool {
for _, ref := range r.Union {
if ref.Object != sn {
// type mismatch
continue
}

switch {
case ref.IsDirect(), ref.IsWildcard():
if len(sr) == 0 {
return true
}
case ref.IsSubject() && lo.Contains(sr, ref.Relation):
return true
}
}
return false
})

return lo.Keys(matches), nil
}
66 changes: 66 additions & 0 deletions cache/cache_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package cache_test

import (
"fmt"
"strings"
"testing"

"github.com/aserto-dev/azm/cache"
v3 "github.com/aserto-dev/azm/v3"
"github.com/samber/lo"
"github.com/stretchr/testify/require"
)

type (
ON = cache.ObjectName
RN = cache.RelationName
)

type testCase struct {
on ON
sn ON
sr []RN
expected []RN
}

func (t *testCase) name() string {
name := fmt.Sprintf("%s#?@%s", t.on, t.sn)
switch len(t.sr) {
case 0:
return name
case 1:
return fmt.Sprintf("%s#%s", name, t.sr[0])
default:
srs := strings.Join(lo.Map(t.sr, func(sr RN, _ int) string { return sr.String() }), "|")
return fmt.Sprintf("%s#[%s]", name, srs)
}
}

func TestAssignableRelations(t *testing.T) {
m, err := v3.LoadFile("./cache_test.yaml")
require.NoError(t, err)
require.NotNil(t, m)

c := cache.New(m)

tests := []*testCase{
{"machine", "user", nil, []RN{"owner"}},
{"machine", "tenant", nil, nil},
{"group", "group", []RN{"member"}, []RN{"member", "guest"}},
{"group", "user", nil, []RN{"member", "guest"}},
{"tenant", "user", nil, []RN{"owner", "admin", "viewer"}},
{"tenant", "group", nil, nil},
{"tenant", "machine", nil, nil},
{"tenant", "machine", []RN{"owner"}, []RN{"log_writer", "data_reader"}},
}

for _, test := range tests {
t.Run(test.name(), func(tt *testing.T) {
assert := require.New(tt)
actual, err := c.AssignableRelations(test.on, test.sn, test.sr...)
assert.NoError(err)
assert.Equal(len(test.expected), len(actual))
assert.Subset(test.expected, actual)
})
}
}
27 changes: 27 additions & 0 deletions cache/cache_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# yaml-language-server: $schema=manifest.json
---

# model
model:
version: 3

types:
user: {}

machine:
relations:
owner: user

group:
relations:
member: user | group#member
guest: user | group#member

tenant:
relations:
owner: user
admin: user | group#member
viewer: user | group#member | group#guest

log_writer: machine#owner
data_reader: machine#owner
Loading

0 comments on commit ff93a1d

Please sign in to comment.