From e4e381b8a4f756a772c5bfda3f04221ea82135dc Mon Sep 17 00:00:00 2001 From: aarifullin Date: Thu, 7 Dec 2023 18:46:12 +0300 Subject: [PATCH] [#26] schema: Add resource name validation method Close #26 Signed-off-by: Airat Arifullin --- docs/resource.md | 20 ++++++ schema/native/util/validation.go | 45 +++++++++++++ schema/native/util/validation_test.go | 97 +++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 docs/resource.md create mode 100644 schema/native/util/validation.go create mode 100644 schema/native/util/validation_test.go diff --git a/docs/resource.md b/docs/resource.md new file mode 100644 index 0000000..4ae339f --- /dev/null +++ b/docs/resource.md @@ -0,0 +1,20 @@ +# Resource + +From the point of the access policy engine, a resource is an object to which a request is being performed. +This can be an object in a container within a namespace, or all objects in a container, +or all containers within the root namespace etc. + +A resource can be viewed from two sides: + - As part of a [request](../pkg/resource/resource.go). In this case a resource has a name and properties. + - As part of rule [chain](../pkg/chain/chain.go): a resource has just a name. + +## Resource name + +A resource name must have a such format that can be processed by a chain router that matches a request +either with local overrides or with rules within policy contract to get if this request is allowed to be performed. +The main idea of this format is for the chain router to match by full name (`native:object//cnrID/objID`) or +wildcard (`native:object//cnrID/*`). + +Check out formats that are defined in the schema: [native formats](../schema/native/consts.go), [s3 formats](../schema/s3/consts.go). +You should validate a resource name using [util](../schema/native/util/validation.go) before instantiating a request or +before putting it to either to local override storage or the policy contract storage. \ No newline at end of file diff --git a/schema/native/util/validation.go b/schema/native/util/validation.go new file mode 100644 index 0000000..5ffccb2 --- /dev/null +++ b/schema/native/util/validation.go @@ -0,0 +1,45 @@ +package util + +import ( + "strings" + + "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" +) + +var nativePatterns = []string{ + native.ResourceFormatNamespaceObjects, native.ResourceFormatNamespaceContainerObjects, + native.ResourceFormatNamespaceContainerObject, native.ResourceFormatRootObjects, + native.ResourceFormatRootContainerObjects, native.ResourceFormatRootContainerObject, + native.ResourceFormatAllObjects, native.ResourceFormatNamespaceContainer, + native.ResourceFormatNamespaceContainers, native.ResourceFormatRootContainer, + native.ResourceFormatRootContainers, native.ResourceFormatAllContainers, +} + +func match(resource, pattern string) bool { + rTokens := strings.Split(resource, "/") + pToken := strings.Split(pattern, "/") + + if len(rTokens) != len(pToken) { + return false + } + + for i := range rTokens { + if pToken[i] == "%s" { + continue + } + if pToken[i] != rTokens[i] { + return false + } + } + + return true +} + +func IsNativeResourceNameValid(resource string) bool { + for _, pattern := range nativePatterns { + if match(resource, pattern) { + return true + } + } + return false +} diff --git a/schema/native/util/validation_test.go b/schema/native/util/validation_test.go new file mode 100644 index 0000000..8114935 --- /dev/null +++ b/schema/native/util/validation_test.go @@ -0,0 +1,97 @@ +package util + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var tests = []struct { + name string + expected bool + resource string +}{ + { + name: "ResourceFormatNamespaceObjects", + expected: true, + resource: "native:object/RootNamespace/*", + }, + { + name: "ResourceFormatNamespaceContainerObjects", + expected: true, + resource: "native:object/RootNamespace/BzQw5HH3feoxFDD5tCT87Y1726qzgLfxEE7wgtoRzB3R/*", + }, + { + name: "ResourceFormatNamespaceContainerObject", + expected: true, + resource: "native:object/RootNamespace/BzQw5HH3feoxFDD5tCT87Y1726qzgLfxEE7wgtoRzB3R/AeZa5HH3feoxFDD5tCT87Y1726qzgLfxEE7wgtoRzB4E", + }, + { + name: "ResourceFormatRootObjects", + expected: true, + resource: "native:object//*", + }, + { + name: "ResourceFormatRootContainerObjects", + expected: true, + resource: "native:object//BzQw5HH3feoxFDD5tCT87Y1726qzgLfxEE7wgtoRzB3R/*", + }, + { + name: "ResourceFormatRootContainerObject", + expected: true, + resource: "native:object//BzQw5HH3feoxFDD5tCT87Y1726qzgLfxEE7wgtoRzB3R/AeZa5HH3feoxFDD5tCT87Y1726qzgLfxEE7wgtoRzB4E", + }, + { + name: "ResourceFormatAllObjects", + expected: true, + resource: "native:object/*", + }, + { + name: "ResourceFormatNamespaceContainer", + expected: true, + resource: "native:container/RootNamespace/BzQw5HH3feoxFDD5tCT87Y1726qzgLfxEE7wgtoRzB3R", + }, + { + name: "ResourceFormatNamespaceContainers", + expected: true, + resource: "native:container/RootNamespace/*", + }, + { + name: "ResourceFormatRootContainers", + expected: true, + resource: "native:container//*", + }, + { + name: "ResourceFormatAllContainers", + expected: true, + resource: "native:container/*", + }, + { + name: "Invalid resource 1", + expected: false, + resource: "native:::container/*", + }, + { + name: "Invalid resource 2", + expected: false, + resource: "native:container/RootNamespace/w5HH3feoxFDD5tCTtoRzB3R/Bz726qzgLfxEE7wgtoRzB3R/RootNamespace", + }, +} + +func TestIsNativeResourceNameValid(t *testing.T) { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.expected, IsNativeResourceNameValid(test.resource)) + }) + } +} + +func BenchmarkIsNativeResourceNameValid(b *testing.B) { + for _, test := range tests { + b.Run(test.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = IsNativeResourceNameValid(test.resource) + } + }) + } +} -- 2.45.2