Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(policy): policy unsafe namespace RPCs wired up to database #1018

Merged
merged 31 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
56ec801
feat(policy): add unsafe service protos
jakedoublev Jun 18, 2024
c35f5ce
proto gencode for unsafe RPCs
jakedoublev Jun 18, 2024
6db8b86
remove unused proto imports
jakedoublev Jun 18, 2024
57575d6
Merge branch 'main' into feat/unsafe-policy
jakedoublev Jun 18, 2024
c5dae3f
feat(policy): unsafe service stubbed RPCs and registration
jakedoublev Jun 18, 2024
96b923d
asserted error message fix
jakedoublev Jun 18, 2024
514df97
stubbed service registration of unsafe service
jakedoublev Jun 18, 2024
3800402
add unsafe service to only org-admin role of casbin defaultPolicy
jakedoublev Jun 18, 2024
19e47f0
Merge branch 'main' into feat/unsafe-policy
jakedoublev Jun 18, 2024
4a79919
Merge branch 'feat/unsafe-policy' into feat/unsafe-endpoints
jakedoublev Jun 18, 2024
3448d63
underscore context arguments in stubs for now
jakedoublev Jun 18, 2024
ad8367c
Merge branch 'main' into feat/unsafe-policy
jakedoublev Jun 18, 2024
19446a4
Merge branch 'feat/unsafe-policy' into feat/unsafe-endpoints
jakedoublev Jun 18, 2024
30f8996
require policy object names/values along with id when deleting, and a…
jakedoublev Jun 18, 2024
1fcca2e
use fqn instead of object name/value
jakedoublev Jun 18, 2024
1079321
Merge branch 'main' into feat/unsafe-policy
jakedoublev Jun 18, 2024
7326a2a
Merge branch 'feat/unsafe-policy' into feat/unsafe-endpoints
jakedoublev Jun 18, 2024
10a173f
add db calls in namespace RPCs
jakedoublev Jun 18, 2024
fd19be2
cascade deletion constraints during migrations WIP
jakedoublev Jun 18, 2024
5070ec9
add migration
jakedoublev Jun 19, 2024
a68444f
fix function rename in tests
jakedoublev Jun 19, 2024
0716114
Merge branch 'main' into feat/unsafe-ns
jakedoublev Jun 20, 2024
78d8ff8
ensure to make get call in namespace deletion to check for not found …
jakedoublev Jun 20, 2024
e899aea
integration tests
jakedoublev Jun 20, 2024
576a7fa
Merge branch 'main' into feat/unsafe-ns
jakedoublev Jun 20, 2024
a3aa67f
improve unsafe update test
jakedoublev Jun 20, 2024
e82207e
handle members values cascade delete in migration
jakedoublev Jun 20, 2024
cb96a91
ensure fqn was updated
jakedoublev Jun 20, 2024
3c384d9
return entire updated object on namespace unsafe update
jakedoublev Jun 20, 2024
8afadcb
cleanup
jakedoublev Jun 20, 2024
03dc27e
remove double roundtrip to get namespace before unsafe deletion
jakedoublev Jun 21, 2024
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
288 changes: 243 additions & 45 deletions service/integration/namespaces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,39 +210,6 @@ func (s *NamespacesSuite) Test_UpdateNamespace_DoesNotExist_ShouldFail() {
s.Nil(ns)
}

func (s *NamespacesSuite) Test_DeleteNamespace() {
testData := s.getActiveNamespaceFixtures()

// Deletion should fail when the namespace is referenced as FK in attribute(s)
for _, ns := range testData {
deleted, err := s.db.PolicyClient.DeleteNamespace(s.ctx, ns.ID)
s.Require().Error(err)
s.Require().ErrorIs(err, db.ErrForeignKeyViolation)
s.Nil(deleted)
}

// Deletion should succeed when NOT referenced as FK in attribute(s)
n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "deleting-namespace.com"})
s.Require().NoError(err)
s.NotEqual("", n)

deleted, err := s.db.PolicyClient.DeleteNamespace(s.ctx, n.GetId())
s.Require().NoError(err)
s.NotNil(deleted)

// Deleted namespace should not be found on List
gotNamespaces, err := s.db.PolicyClient.ListNamespaces(s.ctx, policydb.StateActive)
s.Require().NoError(err)
s.NotNil(gotNamespaces)
for _, ns := range gotNamespaces {
s.NotEqual(n.GetId(), ns.GetId())
}

// Deleted namespace should not be found on Get
_, err = s.db.PolicyClient.GetNamespace(s.ctx, n.GetId())
s.Require().Error(err)
}

func (s *NamespacesSuite) Test_DeactivateNamespace() {
n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "deactivating-namespace.com"})
s.Require().NoError(err)
Expand Down Expand Up @@ -430,18 +397,6 @@ func (s *NamespacesSuite) Test_DeactivateNamespace_Cascades_ToAttributesAndValue
s.False(gotVal.GetActive().GetValue())
}

func (s *NamespacesSuite) Test_DeleteNamespace_DoesNotExist_ShouldFail() {
ns, err := s.db.PolicyClient.DeleteNamespace(s.ctx, nonExistentNamespaceID)
s.Require().Error(err)
s.Require().ErrorIs(err, db.ErrNotFound)
s.Nil(ns)

ns, err = s.db.PolicyClient.DeleteNamespace(s.ctx, nonExistentNamespaceID)
s.Require().Error(err)
s.Require().ErrorIs(err, db.ErrNotFound)
s.Nil(ns)
}

func (s *NamespacesSuite) Test_DeactivateNamespace_DoesNotExist_ShouldFail() {
ns, err := s.db.PolicyClient.DeactivateNamespace(s.ctx, nonExistentNamespaceID)
s.Require().Error(err)
Expand Down Expand Up @@ -478,6 +433,249 @@ func (s *NamespacesSuite) Test_DeactivateNamespace_AllAttributesDeactivated() {
}
}

func (s *NamespacesSuite) Test_UnsafeDeleteNamespace_Cascades() {
// create namespace, with an attribute and values
n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "deleting-namespace.com"})
s.Require().NoError(err)
s.NotNil(n)

attr := &attributes.CreateAttributeRequest{
Name: "test__deleting-attribute",
NamespaceId: n.GetId(),
Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY,
}
createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, attr)
s.Require().NoError(err)
s.NotNil(createdAttr)

val := &attributes.CreateAttributeValueRequest{
Value: "test__deleting-value",
}
createdVal, err := s.db.PolicyClient.CreateAttributeValue(s.ctx, createdAttr.GetId(), val)
s.Require().NoError(err)
s.NotNil(createdVal)

val = &attributes.CreateAttributeValueRequest{
Value: "test__deleting-value-2",
}
createdVal2, err := s.db.PolicyClient.CreateAttributeValue(s.ctx, createdAttr.GetId(), val)
s.Require().NoError(err)
s.NotNil(createdVal2)

// delete the namespace
deleted, err := s.db.PolicyClient.UnsafeDeleteNamespace(s.ctx, n.GetId())
s.Require().NoError(err)
s.NotNil(deleted)

// Deleted namespace should not be found on GET
ns, err := s.db.PolicyClient.GetNamespace(s.ctx, n.GetId())
s.Require().Error(err)
s.Require().ErrorIs(err, db.ErrNotFound)
s.Nil(ns)

// Deleted attribute should not be found on GET
a, err := s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId())
s.Require().Error(err)
s.Require().ErrorIs(err, db.ErrNotFound)
s.Nil(a)

// Deleted values should not be found on GET
v, err := s.db.PolicyClient.GetAttributeValue(s.ctx, createdVal.GetId())
s.Require().Error(err)
s.Require().ErrorIs(err, db.ErrNotFound)
s.Nil(v)
v, err = s.db.PolicyClient.GetAttributeValue(s.ctx, createdVal2.GetId())
s.Require().Error(err)
s.Require().ErrorIs(err, db.ErrNotFound)
s.Nil(v)
}

func (s *NamespacesSuite) Test_UnsafeDeleteNamespace_DoesNotExist_ShouldFail() {
ns, err := s.db.PolicyClient.UnsafeDeleteNamespace(s.ctx, nonExistentNamespaceID)
s.Require().Error(err)
s.Require().ErrorIs(err, db.ErrNotFound)
s.Nil(ns)
}

func (s *NamespacesSuite) Test_UnsafeDeleteNamespace_ShouldBeAbleToRecreateDeletedNamespace() {
// create namespace
n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "deleting-namespace.com"})
s.Require().NoError(err)
s.NotNil(n)

// delete the namespace
_, err = s.db.PolicyClient.UnsafeDeleteNamespace(s.ctx, n.GetId())
s.Require().NoError(err)

// create the namespace again
n, err = s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "deleting-namespace.com"})
s.Require().NoError(err)
s.NotNil(n)
}

func (s *NamespacesSuite) Test_UnsafeReactivateNamespace_SetsActiveStatusOfNamespace() {
// create a namespace
n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "reactivating-namespace.com"})
s.Require().NoError(err)
s.NotNil(n)

// deactivate the namespace
_, err = s.db.PolicyClient.DeactivateNamespace(s.ctx, n.GetId())
s.Require().NoError(err)

// test that it's deactivated
deactivated, err := s.db.PolicyClient.GetNamespace(s.ctx, n.GetId())
s.Require().NoError(err)
s.NotNil(deactivated)
s.False(deactivated.GetActive().GetValue())

// reactivate the namespace
reactivated, err := s.db.PolicyClient.UnsafeReactivateNamespace(s.ctx, n.GetId())
s.Require().NoError(err)
s.NotNil(reactivated)

// test that it's active
active, err := s.db.PolicyClient.GetNamespace(s.ctx, n.GetId())
s.Require().NoError(err)
s.NotNil(active)
s.True(active.GetActive().GetValue())

// test that the namespace is found in the list of active namespaces
gotNamespaces, err := s.db.PolicyClient.ListNamespaces(s.ctx, policydb.StateActive)
s.Require().NoError(err)
s.NotNil(gotNamespaces)
found := false
for _, ns := range gotNamespaces {
if n.GetId() == ns.GetId() {
found = true
break
}
}
s.True(found)

// test that the namespace is not found in the list of inactive namespaces
gotNamespaces, err = s.db.PolicyClient.ListNamespaces(s.ctx, policydb.StateInactive)
s.Require().NoError(err)
s.NotNil(gotNamespaces)
found = false
for _, ns := range gotNamespaces {
if n.GetId() == ns.GetId() {
found = true
break
}
}
s.False(found)
}

func (s *NamespacesSuite) Test_UnsafeReactivateNamespace_DoesNotExist_ShouldFail() {
ns, err := s.db.PolicyClient.UnsafeReactivateNamespace(s.ctx, nonExistentNamespaceID)
s.Require().Error(err)
s.Require().ErrorIs(err, db.ErrNotFound)
s.Nil(ns)

ns, err = s.db.PolicyClient.UnsafeReactivateNamespace(s.ctx, nonExistentNamespaceID)
s.Require().Error(err)
s.Require().ErrorIs(err, db.ErrNotFound)
s.Nil(ns)
}

func (s *NamespacesSuite) Test_UnsafeReactivateNamespace_ShouldNotReactivateChildren() {
// create a namespace
n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "reactivating-ns.io"})
s.Require().NoError(err)
s.NotNil(n)

// create an attribute definition
attr, err := s.db.PolicyClient.CreateAttribute(s.ctx, &attributes.CreateAttributeRequest{
Name: "reactivating-attr",
NamespaceId: n.GetId(),
Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF,
})
s.Require().NoError(err)
s.NotNil(attr)

// create a value for the attribute definition
val, err := s.db.PolicyClient.CreateAttributeValue(s.ctx, attr.GetId(), &attributes.CreateAttributeValueRequest{
Value: "reactivating-val",
})
s.Require().NoError(err)
s.NotNil(val)

// deactivate the namespace
_, err = s.db.PolicyClient.DeactivateNamespace(s.ctx, n.GetId())
s.Require().NoError(err)

// ensure all are inactive
deactivatedNs, err := s.db.PolicyClient.GetNamespace(s.ctx, n.GetId())
s.Require().NoError(err)
s.NotNil(deactivatedNs)
s.False(deactivatedNs.GetActive().GetValue())

deactivatedAttr, err := s.db.PolicyClient.GetAttribute(s.ctx, attr.GetId())
s.Require().NoError(err)
s.NotNil(deactivatedAttr)
s.False(deactivatedAttr.GetActive().GetValue())

deactivatedVal, err := s.db.PolicyClient.GetAttributeValue(s.ctx, val.GetId())
s.Require().NoError(err)
s.NotNil(deactivatedVal)
s.False(deactivatedVal.GetActive().GetValue())

// reactivate the namespace
_, err = s.db.PolicyClient.UnsafeReactivateNamespace(s.ctx, n.GetId())
s.Require().NoError(err)

// ensure the namespace is active
reactivatedNs, err := s.db.PolicyClient.GetNamespace(s.ctx, n.GetId())
s.Require().NoError(err)
s.NotNil(reactivatedNs)
s.True(reactivatedNs.GetActive().GetValue())

// ensure the attribute definition is still inactive
reactivatedAttr, err := s.db.PolicyClient.GetAttribute(s.ctx, attr.GetId())
s.Require().NoError(err)
s.NotNil(reactivatedAttr)
s.False(reactivatedAttr.GetActive().GetValue())

// ensure the values are still inactive
reactivatedVal, err := s.db.PolicyClient.GetAttributeValue(s.ctx, val.GetId())
s.Require().NoError(err)
s.NotNil(reactivatedVal)
s.False(reactivatedVal.GetActive().GetValue())
}

func (s *NamespacesSuite) Test_UnsafeUpdateNamespace() {
name := "unsafe.gov"
after := "hello.world"
created, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: name})
s.Require().NoError(err)
s.NotNil(created)

updated, err := s.db.PolicyClient.UnsafeUpdateNamespace(s.ctx, created.GetId(), after)
s.Require().NoError(err)
s.NotNil(updated)
s.Equal(created.GetId(), updated.GetId())

got, err := s.db.PolicyClient.GetNamespace(s.ctx, created.GetId())
s.Require().NoError(err)
s.NotNil(got)
s.Equal(after, got.GetName())
s.Equal("https://"+after, got.GetFqn())

// should be able to create original name after unsafely updating
recreated, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: name})
s.Require().NoError(err)
s.NotNil(recreated)
s.NotEqual(created.GetId(), recreated.GetId())
}

func (s *NamespacesSuite) Test_UnsafeUpdateNamespace_DoesNotExist_ShouldFail() {
ns, err := s.db.PolicyClient.UnsafeUpdateNamespace(s.ctx, nonExistentNamespaceID, "does.not.exist")
s.Require().Error(err)
s.Require().ErrorIs(err, db.ErrNotFound)
s.Nil(ns)
}

func TestNamespacesSuite(t *testing.T) {
if testing.Short() {
t.Skip("skipping namespaces integration tests")
Expand Down
Loading
Loading