Skip to content

Commit

Permalink
feat(model/test): support running list users tests (#315)
Browse files Browse the repository at this point in the history
  • Loading branch information
ewanharris authored May 2, 2024
2 parents 0c3e725 + a26b091 commit 09898bd
Show file tree
Hide file tree
Showing 13 changed files with 550 additions and 68 deletions.
1 change: 1 addition & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ linters-settings:
- github.com/openfga/go-sdk
- github.com/openfga/openfga
- github.com/stretchr
- github.com/openfga/api/proto

tagliatelle:
case:
Expand Down
18 changes: 16 additions & 2 deletions example/model.fga.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ tests:
can_view: true
can_write: true
can_share: false
list_objects: # Each check test is made of: a user, an object type and the expected result for one or more relations
list_objects: # Each list objects test is made of: a user, an object type and the expected result for one or more relations
- user: user:anne
type: folder
assertions:
Expand All @@ -68,4 +68,18 @@ tests:
- folder:product-2021
- folder:product-2021Q1
can_write: []
can_share: []
can_share: []
list_users: # Each list user test is made of: an object, a user filter and the expected result for one or more relations
- object: folder:product-2021
user_filter:
- type: user
assertions:
can_view:
users:
- user:anne
- user:beth
excluded_users: []
can_write:
users:
- user:anne
excluded_users: []
22 changes: 21 additions & 1 deletion example/store_abac.fga.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ tests:
user_ip: "192.168.1.0"
assertions:
viewer: false # current time is within granted time interval but the user's ip address is outside the CIDR range
list_objects: # Each check test is made of: a user, an object type and the expected result for one or more relations
list_objects: # Each list objects test is made of: a user, an object type and the expected result for one or more relations
- user: user:anne
type: document
context:
Expand All @@ -63,3 +63,23 @@ tests:
user_ip: "192.168.1.0"
assertions:
viewer: []
list_users: # Each list user test is made of: an object, a user filter and the expected result for one or more relations
- object: document:1
user_filter:
- type: user
context:
current_timestamp: "2023-05-03T21:25:23+00:00"
user_ip: "192.168.0.0"
assertions:
viewer:
users:
- user:anne
- object: document:1
user_filter:
- type: user
context:
current_timestamp: "2023-05-03T21:25:31+00:00"
user_ip: "192.168.0.0"
assertions:
viewer:
users: []
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ require (
github.com/muesli/roff v0.1.0
github.com/nwidger/jsoncolor v0.3.2
github.com/oklog/ulid/v2 v2.1.0
github.com/openfga/api/proto v0.0.0-20240425220334-619029c1d3d3
github.com/openfga/api/proto v0.0.0-20240430203311-36050418a284
github.com/openfga/go-sdk v0.3.6-0.20240430041914-d27ef8fa20b8
github.com/openfga/language/pkg/go v0.0.0-20240429103126-f3e71ca3287d
github.com/openfga/openfga v1.5.3
github.com/openfga/openfga v1.5.4-0.20240430205231-c4953b813b89
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.18.2
Expand Down
18 changes: 8 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v26.0.0+incompatible h1:Ng2qi+gdKADUa/VM+6b6YaY2nlZhk/lVJiKR/2bMudU=
github.com/docker/docker v26.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v26.0.2+incompatible h1:yGVmKUFGgcxA6PXWAokO0sQL22BrQ67cgVjko8tGdXE=
github.com/docker/docker v26.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
Expand Down Expand Up @@ -183,16 +183,14 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/openfga/api/proto v0.0.0-20240425220334-619029c1d3d3 h1:d1Kpom9kYMCJQlImCVHAEUfTcL4WeiEGQHcc5+KYQnk=
github.com/openfga/api/proto v0.0.0-20240425220334-619029c1d3d3/go.mod h1:5LtWOArDX4FlbcfDvBoJAzDEYJKLz/OEUoi+0S2tyM8=
github.com/openfga/go-sdk v0.3.6-0.20240425164425-0ac2df2acad5 h1:hjhR7oRVvpKQ9kbFCcel6vAbO2nlr5nwORn80sVzuFU=
github.com/openfga/go-sdk v0.3.6-0.20240425164425-0ac2df2acad5/go.mod h1:AoMnFlPw65sU/7O4xOPpCb2vXA8ZD9K9xp2hZjcvt4g=
github.com/openfga/api/proto v0.0.0-20240430203311-36050418a284 h1:gA09kLBB/voWQCtCH745P2Lx6ZoI7DDs+XlvLFm9S3M=
github.com/openfga/api/proto v0.0.0-20240430203311-36050418a284/go.mod h1:5LtWOArDX4FlbcfDvBoJAzDEYJKLz/OEUoi+0S2tyM8=
github.com/openfga/go-sdk v0.3.6-0.20240430041914-d27ef8fa20b8 h1:P2S/gfxpoHBW0tDCxu/aC67MniCcVMR6nQtNzuLLelE=
github.com/openfga/go-sdk v0.3.6-0.20240430041914-d27ef8fa20b8/go.mod h1:t2iDiGuJtdyjvMHzkxDsbWbCWOLlVMViPKrU7Sau4K8=
github.com/openfga/language/pkg/go v0.0.0-20240429103126-f3e71ca3287d h1:a8YgRx1RfofFiDbMj/Azwh0UeMbdfUf7OiMqSom/smQ=
github.com/openfga/language/pkg/go v0.0.0-20240429103126-f3e71ca3287d/go.mod h1:wkI4GcY3yNNuFMU2ncHPWqBaF7XylQTkJYfBi2pIpK8=
github.com/openfga/openfga v1.5.3 h1:Uynmlsx3iz/eiP7wW9n2dbwxDh/kiS/27W6C24Y7oyY=
github.com/openfga/openfga v1.5.3/go.mod h1:IcQBDtytjhBxjfJ+1zCzUxQvQa1BB/4Ed+POpZKojTI=
github.com/openfga/openfga v1.5.4-0.20240430205231-c4953b813b89 h1:hPSTLw4uhdpbcM8h9qsU2WjM6ypF8ktwhAepSbouRfE=
github.com/openfga/openfga v1.5.4-0.20240430205231-c4953b813b89/go.mod h1:k6hBuz6L6tJcqZFogrx0p5qRQF1V3i9qh92i2GJbpNE=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtosvtEhg=
Expand All @@ -205,8 +203,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/pressly/goose/v3 v3.19.2 h1:z1yuD41jS4iaqLkyjkzGkKBz4rgyz/BYtCyMMGHlgzQ=
github.com/pressly/goose/v3 v3.19.2/go.mod h1:BHkf3LzSBmO8E5FTMPupUYIpMTIh/ZuQVy+YTfhZLD4=
github.com/pressly/goose/v3 v3.20.0 h1:uPJdOxF/Ipj7ABVNOAMJXSxwFXZGwMGHNqjC8e61VA0=
github.com/pressly/goose/v3 v3.20.0/go.mod h1:BRfF2GcG4FTG12QfdBVy3q1yveaf4ckL9vWwEcIO3lA=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
Expand Down
91 changes: 91 additions & 0 deletions internal/storetest/conversion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package storetest

import (
"fmt"
"strings"

pb "github.com/openfga/api/proto/openfga/v1"
openfga "github.com/openfga/go-sdk"
"github.com/openfga/go-sdk/client"
"google.golang.org/protobuf/types/known/structpb"
)

func convertClientTupleKeysToProtoTupleKeys(
tuples []client.ClientContextualTupleKey,
) ([]*pb.TupleKey, error) {
pbTuples := []*pb.TupleKey{}

for index := 0; index < len(tuples); index++ {
tuple := tuples[index]
tpl := pb.TupleKey{
User: tuple.User,
Relation: tuple.Relation,
Object: tuple.Object,
}

if tuple.Condition != nil {
conditionContext, err := structpb.NewStruct(tuple.Condition.GetContext())
if err != nil {
return nil, fmt.Errorf("failed to construct a proto struct: %w", err)
}

tpl.Condition = &pb.RelationshipCondition{
Name: tuple.Condition.Name,
Context: conditionContext,
}
}

pbTuples = append(pbTuples, &tpl)
}

return pbTuples, nil
}

func convertStoreObjectToObject(object string) (openfga.FgaObject, *pb.Object) {
splitObject := strings.Split(object, ":")

return openfga.FgaObject{
Type: splitObject[0],
Id: splitObject[1],
}, &pb.Object{
Type: splitObject[0],
Id: splitObject[1],
}
}

func convertPbUsersToStrings(users []*pb.User) []string {
simpleUsers := []string{}

for _, user := range users {
switch typedUser := user.GetUser().(type) {
case *pb.User_Object:
simpleUsers = append(simpleUsers, typedUser.Object.GetType()+":"+typedUser.Object.GetId())
case *pb.User_Userset:
simpleUsers = append(
simpleUsers,
typedUser.Userset.GetType()+":"+typedUser.Userset.GetId()+"#"+typedUser.Userset.GetRelation(),
)
case *pb.User_Wildcard:
simpleUsers = append(simpleUsers, typedUser.Wildcard.GetType()+":*")
}
}

return simpleUsers
}

func convertOpenfgaUsers(users []openfga.User) []string {
simpleUsers := []string{}

for _, user := range users {
switch {
case user.Object != nil:
simpleUsers = append(simpleUsers, user.Object.Type+":"+user.Object.Id)
case user.Userset != nil:
simpleUsers = append(simpleUsers, user.Userset.Type+":"+user.Userset.Id+"#"+user.Userset.Relation)
case user.Wildcard != nil:
simpleUsers = append(simpleUsers, user.Wildcard.Type+":*")
}
}

return simpleUsers
}
139 changes: 139 additions & 0 deletions internal/storetest/conversion_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package storetest

import (
"testing"

pb "github.com/openfga/api/proto/openfga/v1"
openfga "github.com/openfga/go-sdk"
"github.com/openfga/go-sdk/client"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestConvertPbUsersToStrings(t *testing.T) {
t.Parallel()

tests := map[string]struct {
input *pb.User
expected string
}{
"User_Object": {
input: &pb.User{User: &pb.User_Object{Object: &pb.Object{Type: "user", Id: "anne"}}},
expected: "user:anne",
},
"User_Userset": {
input: &pb.User{User: &pb.User_Userset{Userset: &pb.UsersetUser{Type: "group", Id: "fga", Relation: "member"}}},
expected: "group:fga#member",
},
"User_Wildcard": {
input: &pb.User{User: &pb.User_Wildcard{Wildcard: &pb.TypedWildcard{Type: "user"}}},
expected: "user:*",
},
}

for name, testcase := range tests {
testcase := testcase

t.Run(name, func(t *testing.T) {
t.Parallel()

got := convertPbUsersToStrings([]*pb.User{testcase.input})

assert.Equal(t, []string{testcase.expected}, got)
})
}
}

func TestConvertOpenfgaUsersToStrings(t *testing.T) {
t.Parallel()

tests := map[string]struct {
input openfga.User
expected string
}{
"User_Object": {
input: openfga.User{Object: &openfga.FgaObject{Type: "user", Id: "anne"}},
expected: "user:anne",
},
"User_Userset": {
input: openfga.User{Userset: &openfga.UsersetUser{Type: "group", Id: "fga", Relation: "member"}},
expected: "group:fga#member",
},
"User_Wildcard": {
input: openfga.User{Wildcard: &openfga.TypedWildcard{Type: "user"}},
expected: "user:*",
},
}

for name, testcase := range tests {
testcase := testcase

t.Run(name, func(t *testing.T) {
t.Parallel()

got := convertOpenfgaUsers([]openfga.User{testcase.input})

assert.Equal(t, []string{testcase.expected}, got)
})
}
}

func TestConvertStoreObjectToObject(t *testing.T) {
t.Parallel()

tests := map[string]struct {
input string
expectedFGAObject openfga.FgaObject
expectedPBObject *pb.Object
}{
"Converts object": {
input: "document:roadmap",
expectedFGAObject: openfga.FgaObject{Type: "document", Id: "roadmap"},
expectedPBObject: &pb.Object{Type: "document", Id: "roadmap"},
},
}

for name, testcase := range tests {
testcase := testcase

t.Run(name, func(t *testing.T) {
t.Parallel()

fgaObject, pbObject := convertStoreObjectToObject(testcase.input)

assert.Equal(t, testcase.expectedFGAObject, fgaObject)
assert.Equal(t, testcase.expectedPBObject, pbObject)
})
}
}

func TestConvertClientTupleKeysToProtoTupleKeys(t *testing.T) {
t.Parallel()

tests := map[string]struct {
input []client.ClientContextualTupleKey
expected []*pb.TupleKey
}{
"User_Object": {
input: []client.ClientContextualTupleKey{
{User: "user:anne", Relation: "owner", Object: "folder:product"},
},
expected: []*pb.TupleKey{
{User: "user:anne", Relation: "owner", Object: "folder:product"},
},
},
}

for name, testcase := range tests {
testcase := testcase

t.Run(name, func(t *testing.T) {
t.Parallel()

tuples, err := convertClientTupleKeysToProtoTupleKeys(testcase.input)

require.NoError(t, err)
assert.Equal(t, testcase.expected, tuples)
})
}
}
1 change: 1 addition & 0 deletions internal/storetest/localstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ func getLocalServerModelAndTuples(

fgaServer, err := server.NewServerWithOpts(
server.WithDatastore(datastore),
server.WithExperimentals(server.ExperimentalEnableListUsers),
)
if err != nil {
return nil, nil, stopServerFn, err //nolint:wrapcheck
Expand Down
Loading

0 comments on commit 09898bd

Please sign in to comment.