diff --git a/.forgejo/workflows/dco.yml b/.forgejo/workflows/dco.yml
index 2374802..6746408 100644
--- a/.forgejo/workflows/dco.yml
+++ b/.forgejo/workflows/dco.yml
@@ -13,9 +13,9 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v3
with:
- go-version: '1.23'
+ go-version: '1.21'
- name: Run commit format checker
- uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3
+ uses: https://git.frostfs.info/TrueCloudLab/dco-go@v2
with:
from: 'origin/${{ github.event.pull_request.base.ref }}'
diff --git a/.forgejo/workflows/tests.yml b/.forgejo/workflows/tests.yml
index 8dffdf9..f66a2c4 100644
--- a/.forgejo/workflows/tests.yml
+++ b/.forgejo/workflows/tests.yml
@@ -11,7 +11,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
- go-version: '1.23'
+ go-version: '1.21'
cache: true
- name: Install linters
@@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- go_versions: [ '1.22', '1.23' ]
+ go_versions: [ '1.20', '1.21' ]
fail-fast: false
steps:
- uses: actions/checkout@v3
@@ -48,7 +48,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
- go-version: '1.23'
+ go-version: '1.21'
cache: true
- name: Run tests
@@ -63,7 +63,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
- go-version: '1.23'
+ go-version: '1.21'
cache: true
- name: Install staticcheck
diff --git a/.gitattributes b/.gitattributes
index a582511..c7a3f7a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,4 +1,2 @@
/**/*.pb.go -diff -merge
/**/*.pb.go linguist-generated=true
-/**/*_easyjson.go -diff -merge
-/**/*_easyjson.go linguist-generated=true
diff --git a/.golangci.yml b/.golangci.yml
index 42b6b36..e10866d 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -12,8 +12,7 @@ run:
# output configuration options
output:
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
- formats:
- - format: tab
+ format: tab
# all available settings of specific linters
linters-settings:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 7e1fef9..aad8e01 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,6 +2,13 @@ ci:
autofix_prs: false
repos:
+ - repo: https://github.com/jorisroovers/gitlint
+ rev: v0.19.1
+ hooks:
+ - id: gitlint
+ stages: [commit-msg]
+ - id: gitlint-ci
+
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
@@ -35,7 +42,7 @@ repos:
hooks:
- id: go-unit-tests
name: go unit tests
- entry: make test GOFLAGS=''
+ entry: make test
pass_filenames: false
types: [go]
language: system
diff --git a/CODEOWNERS b/CODEOWNERS
deleted file mode 100644
index f164173..0000000
--- a/CODEOWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-.* @TrueCloudLab/storage-core-committers @TrueCloudLab/storage-core-developers @TrueCloudLab/storage-services-committers @TrueCloudLab/storage-services-developers
-.forgejo/.* @potyarkin
-Makefile @potyarkin
diff --git a/Makefile b/Makefile
index ca3b85d..debad31 100755
--- a/Makefile
+++ b/Makefile
@@ -1,12 +1,10 @@
#!/usr/bin/make -f
-TRUECLOUDLAB_LINT_VERSION ?= 0.0.6
+TRUECLOUDLAB_LINT_VERSION ?= 0.0.2
TMP_DIR := .cache
OUTPUT_LINT_DIR ?= $(shell pwd)/bin
-LINT_VERSION ?= 1.60.1
+LINT_VERSION ?= 1.55.1
LINT_DIR = $(OUTPUT_LINT_DIR)/golangci-lint-$(LINT_VERSION)-v$(TRUECLOUDLAB_LINT_VERSION)
-EASYJSON_VERSION ?= $(shell go list -f '{{.Version}}' -m github.com/mailru/easyjson)
-EASYJSON_DIR ?= $(shell pwd)/bin/easyjson-$(EASYJSON_VERSION)
# Run all code formatters
fmts: fmt imports
@@ -22,10 +20,9 @@ imports:
@goimports -w .
# Run Unit Test with go test
-test: GOFLAGS ?= "-count=1"
test:
@echo "⇒ Running go test"
- @GOFLAGS="$(GOFLAGS)" go test ./...
+ @go test ./... -count=1
# Activate pre-commit hooks
pre-commit:
@@ -63,15 +60,3 @@ staticcheck-install:
# Run staticcheck
staticcheck-run:
@staticcheck ./...
-
-easyjson-install:
- @rm -rf $(EASYJSON_DIR)
- @mkdir -p $(EASYJSON_DIR)
- @GOBIN=$(EASYJSON_DIR) go install github.com/mailru/easyjson/...@$(EASYJSON_VERSION)
-
-generate:
- @if [ ! -d "$(EASYJSON_DIR)" ]; then \
- make easyjson-install; \
- fi
- find ./ -name "_easyjson.go" -exec rm -rf {} \;
- $(EASYJSON_DIR)/easyjson ./pkg/chain/chain.go
diff --git a/docs/ape.md b/docs/ape.md
deleted file mode 100644
index a239499..0000000
--- a/docs/ape.md
+++ /dev/null
@@ -1,85 +0,0 @@
-# Access policy engine
-
-## General overview
-
-### Purpose
-
-Access policy engine (APE) is aimed at checking if a request can be performed over a resource by looking up the set chains of rules.
-
-#### Terms
-
-| Term | Description | Structure overview |
-| -------------- | -------------------------------------------------------------- | -------------- |
-| `Request` | The action that is being performed on the `Resource`. |
`Operation` - `GetObject`,`PutObject` etc.; `Properties` - actor's public key, actor's attributes; `Resource`. |
-| `Resource` | The object that the request is being performed on. Check also [resource.md](./resource.md). | `Name` - strictly formatted string value; `Properties`. |
-| `Chain` | A chain of `Rule`-s defined for a specific target. Chains are strictly distinguished by `Name`-s , i.e. chains with name `ingress` are not intersected with chains with name `s3`. Chains are stored in serialized format. | Base64-encoded `ID`; List of `Rule`-s; `MatchType` - defines rule status selection priority. |
-| `Rule` | `Rule` defines which status is returned if `Request` matches all conditions. | `Status`: `Allow`, `AccessDenied`, `QuotaLimitReached`, `NoRuleFound`; `Actions` - operation defined by a schema (`GetObject`, `PutContainer` etc.); `Resources`; `Any` - if `true` then `Reqeust` matches `Rule` if any `Condition` is `true`; `Conditions`. |
-| `Name` | `Name` of a chain (do not confuse with chain ID). `Name` defines a layer of `Chain`'s usage, so chains are distinguished by `Name`-s. Basically, `Name` refers to a protocol. | String value (`ingress`, `s3`, `iam`). |
-| `Target` | A scope of request. `Target` can be either simple (only namespace; only container; only user; only groups) or compound (namespace + container). | `Namespace`; `Container`; `User`; `Groups`. |
-| `Engine` | `Engine` checks a request in a scope defined by `Target`. First, it is trying to match a request with rules defined in `LocalOverrideStorage` and, then, in `MorphRuleChainStorage`. | `LocalOverrideStorage` - chains stored in the local override storage have the highest priority `MorphRuleChainStorage` - basically, chains stored in `Policy` contract; `ChainRouter` - looks up chains and try to match them with `Request`. |
-
-#### Details
-
-Here some entities are overviewed in more detail.
-
-##### Resource
-
-`Resource`'s name is strictly formatted, the format is defined by a schema (`native`, `aws` etc.). Examples:
-
-```bash
-# The resource is the particular object with the address within Root namespace
-native:object//HRwWbb1bJjRms33kkA21hy4JdPfARaH3fW9NfuNN6Fgj/EbxzAdz5LB4uqxuz6crWKAumBNtZyK2rKsqQP7TdZvwr
-# The resource is all objects within the container within Root namespace
-native:object//HRwWbb1bJjRms33kkA21hy4JdPfARaH3fW9NfuNN6Fgj/*
-# The resource is the particular container within the namespace
-native:container/namespace1/HRwWbb1bJjRms33kkA21hy4JdPfARaH3fW9NfuNN6Fgj
-# The resource is all containers within the namespace
-native:container/namespace1/*
-```
-
-##### Rule
-
-`Rule` works out if:
-
- 1. a requests's operation matches the rule's `Actions`;
- 2. resource name matches the rule's `Resources`;
- 3. if all (or at least one if `Any=true`) conditions in `Condition` is met. Each condition defines how to retrieve
-and compare the retrieved value. If `Condition`'s `Object` is set to `Resource` then the value is retrieved from the
-resource's properties (example: container zone attribute). If `Object` is set to `Request`, the it's retrieved from the request's properties (example: actor's public key).
-
-###### Name matching
-
-`Resource`'s name in `Rule` may contain wildcard '*' that can be considered as a regular expression:
-
-```bash
-# The resource is all objects within the container within Root namespace
-native:object//HRwWbb1bJjRms33kkA21hy4JdPfARaH3fW9NfuNN6Fgj/*
-```
-
-If an incoming request has such a resource name, then names are matched:
-
-```bash
-# The resource is all objects within the container within Root namespace
-native:object//HRwWbb1bJjRms33kkA21hy4JdPfARaH3fW9NfuNN6Fgj/EbxzAdz5LB4uqxuz6crWKAumBNtZyK2rKsqQP7TdZvwr
-```
-
-If the incoming request has such a resource name that specifies a container's object within namespace, for instance, `namespicy`,
-then matching does not work out:
-
-```bash
-# The resource is all objects within the container within `namespicy` namespace:
-native:object/namespicy/HRwWbb1bJjRms33kkA21hy4JdPfARaH3fW9NfuNN6Fgj/EbxzAdz5LB4uqxuz6crWKAumBNtZyK2rKsqQP7TdZvwr
-```
-
-##### Engine
-
-`Engine` is trying to match the request against **the target** looking up chain rules, firstly, in `LocalOverrideStorage` (these rules are also known as *local overrides*) and then in `MorphRuleChainStorage` (contract `Policy`). Both storages iterate chain rules according to the specified priority of the targets: `namespace` -> `container` -> `user` -> `groups`.
-
-#### Diagrams
-
-The diagram demonstrates a scenario in Storage node. The request `A` cannot be performed as APE matched
-the request and returned `Access Denied` status. The request `B` is allowed and the client gets `OK` status.
-
-
-The diagram demonstrates a complex scenario with S3, IAM and Storage node.
-
diff --git a/docs/images/ape/s3_ape.puml b/docs/images/ape/s3_ape.puml
deleted file mode 100644
index cf4e2ec..0000000
--- a/docs/images/ape/s3_ape.puml
+++ /dev/null
@@ -1,61 +0,0 @@
-@startuml s3 ape
-
-participant "Client" as client
-
-participant "IAM" as iam
-participant "IAM -> APE converter" as converter
-
-box "S3" #HotPink
- participant "S3 gateway" as s3
-end box
-
-box "Access Policy Engine (as s3 middleware)" #LightPink
- participant "Local override storage" as s3localOverrides
- participant "Chain router" as s3chainRouter
-end box
-
-box "Policy contract (shared)"
- participant "Morph rule storage" as morphRuleStorage
-end box
-
-box "Access Policy Engine (as storage middleware)" #LightGreen
- participant "Chain Router" as storageChainRouter
- participant "Local override storage" as storageLocalOverrides
-end box
-
-box "Storage node" #Green
- participant "Object service" as obj
- participant "Control service" as control
-end box
-
-group Request IAM to set a policy
- client -> iam : Set IAM policy
- iam -> converter : Convert IAM policy
- converter -> iam : Return APE chain
- iam -> morphRuleStorage : Store IAM policy and APE chain
- iam -> s3localOverrides : Set S3 local overrides
- iam -> client : OK
-end
-
-group Request S3 to set a policy
- client -> s3 : Set bucket policy
- s3 -> converter : Convert IAM policy
- converter -> s3 : Return APE chain
- s3 -> morphRuleStorage : Store bucket policy and APE chain
- s3 -> client : OK
-end
-
-group Get object
- client -> s3: GetObject
- s3 -> s3chainRouter: Check if APE allows request for S3
- note over s3chainRouter: matching the request with overrides and rules
- s3chainRouter -> s3: Status: ALLOW
- s3 -> obj: Get object
- obj -> storageChainRouter: Check if APE allows the request
- note over storageChainRouter : matching the request with overrides and rules
- storageChainRouter -> obj: Status: ALLOW
- obj -> s3: Response: OK, Object
- s3 -> client: Response: OK, Object
-end
-
-@enduml
\ No newline at end of file
diff --git a/docs/images/ape/s3_ape.svg b/docs/images/ape/s3_ape.svg
deleted file mode 100644
index bd119c0..0000000
--- a/docs/images/ape/s3_ape.svg
+++ /dev/null
@@ -1,73 +0,0 @@
-S3 Access Policy Engine (as s3 middleware) Policy contract (shared) Access Policy Engine (as storage middleware) Storage node Client Client IAM IAM IAM -> APE converter IAM -> APE converter S3 gateway S3 gateway Local override storage Local override storage Chain router Chain router Morph rule storage Morph rule storage Chain Router Chain Router Local override storage Local override storage Object service Object service Control service Control service Request IAM to set a policy Set IAM policy Convert IAM policy Return APE chain Store IAM policy and APE chain Set S3 local overrides OK Request S3 to set a policy Set bucket policy Convert IAM policy Return APE chain Store bucket policy and APE chain OK Get object GetObject Check if APE allows request for S3 matching the request with overrides and rules Status: ALLOW Get object Check if APE allows the request matching the request with overrides and rules Status: ALLOW Response: OK, Object Response: OK, Object
\ No newline at end of file
diff --git a/docs/images/ape/storage_node_ape.puml b/docs/images/ape/storage_node_ape.puml
deleted file mode 100644
index ddf55e3..0000000
--- a/docs/images/ape/storage_node_ape.puml
+++ /dev/null
@@ -1,46 +0,0 @@
-@startuml storage node ape
-!pragma teoz true
-
-participant "Administrator" as administrator
-participant "Client" as client
-
-box "Storage node" #Green
- participant "Object service" as obj
- participant "Control service" as control
-end box
-
-box "Access Policy Engine" #LightGreen
- participant "Local override storage" as localOverrides
- participant "Chain Router" as chainRouter
- participant "Morph rule storage" as morphRuleStorage
-end box
-
-group Set local override
- client -> control: Add local override
- control -> localOverrides: Save override in DB
- localOverrides -> control: OK
- control -> client: OK
-end
-
-group Update state in Policy contract
- administrator -> morphRuleStorage: Add chain
- morphRuleStorage -> administrator: OK
-end
-
-group Perform a request A
- client -> obj : Sending a request
- obj -> chainRouter: Check if APE allows the request
- note over chainRouter : Fetches local overrides and rules defined for a target/targets and looks for a match
- chainRouter -> obj: APE returns status: "ACCESS DENIED"
- obj -> client: Response: "the request is denied"
-end
-
-group Perform a request B
- client -> obj : Sending a request
- obj -> chainRouter: Check if APE allows the request
- note over chainRouter : Fetches local overrides and rules defined for a target/targets and looks for a match
- chainRouter -> obj: APE returns status: "ALLOW"
- obj -> client: Response: "OK"
-end
-
-@enduml
\ No newline at end of file
diff --git a/docs/images/ape/storage_node_ape.svg b/docs/images/ape/storage_node_ape.svg
deleted file mode 100644
index 6e84081..0000000
--- a/docs/images/ape/storage_node_ape.svg
+++ /dev/null
@@ -1,58 +0,0 @@
-Storage node Access Policy Engine Administrator Client Object service Control service Local override storage Chain Router Morph rule storage Administrator Client Object service Control service Local override storage Chain Router Morph rule storage Set local override Add local override Save override in DB OK OK Update state in Policy contract Add chain OK Perform a request A Sending a request Check if APE allows the request Fetches local overrides and rules defined for a target/targets and looks for a match APE returns status: "ACCESS DENIED" Response: "the request is denied" Perform a request B Sending a request Check if APE allows the request Fetches local overrides and rules defined for a target/targets and looks for a match APE returns status: "ALLOW" Response: "OK"
\ No newline at end of file
diff --git a/docs/policy_converters.md b/docs/policy_converters.md
deleted file mode 100644
index 5a2b1ce..0000000
--- a/docs/policy_converters.md
+++ /dev/null
@@ -1,439 +0,0 @@
-# Policy converters
-
-This repository contains converters that provide opportunities to
-transform [AWS IAM policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies.html) to inner
-FrostFS policy format. This document describes such transformations.
-
-## FrostFS
-
-As it was mentioned there are converters that transform AWS IAM policies to FrostFS.
-Here common examples of AWS:
-
-```json
-{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Action": [
- "s3:*"
- ],
- "Resource": "*"
- }
- ]
-}
-```
-
-and FrostFS:
-
-```json
-{
- "ID": "c29tZS1pZA==",
- "Rules": [
- {
- "Status": "Allow",
- "Actions": {
- "Inverted": false,
- "Names": [
- "s3:*"
- ]
- },
- "Resources": {
- "Inverted": false,
- "Names": [
- "*"
- ]
- },
- "Any": false,
- "Condition": null
- }
- ],
- "MatchType": "DenyPriority"
-}
-```
-
-policies.
-
-Despite there is only one FrostFS format, we have two converters (`s3` and `native`). The reason is S3 gateway and
-Storage node have different actions and resource naming:
-
-* S3 has [a lot of methods](https://docs.aws.amazon.com/AmazonS3/latest/API/API_Operations.html) and operates with
- bucket/object
-* Storage node has only 6 container and 7 object methods and operates container/object (that has different format)
-
-The following sections describe each transformation more precisely ([common](#common) sections contains shared concepts)
-
-### Common
-
-#### Fields
-
-Rough json main fields mapping:
-
-| AWS policy field | FrostFS policy field | Comment |
-|------------------|----------------------|------------------------------------------------------------------|
-| `Version` | - | Not applicable |
-| `Statement` | `Rules` | |
-| `Effect` | `Status` | |
-| `Action` | `Actions.Names` | `Actions.Inverted` = false |
-| `NotAction` | `Actions.Names` | `Actions.Inverted` = true |
-| `Resource` | `Resources.Names` | `Resources.Inverted` = false |
-| `NotResource` | `Resources.Names` | `Resources.Inverted` = true |
-| `Condition` | `Condition` | `Any` = false, that means the conditions must be hold altogether |
-| `Principal` | - | Expressed via conditions (depends on s3/native converters) |
-
-### Conditions
-
-Each condition in FrostFS policy can add requirements to some request/resource properties
-and consists of the following fields:
-
-| Field | Description |
-|----------|-------------------------------------------------------------------------------------------|
-| `Op` | Condition type operation (`StringEqual`, `NumericEqual` etc) |
-| `Object` | Property type to which condition can be applied (`Request` property, `Resource` property) |
-| `Key` | Property key |
-| `Value` | Property value |
-
-Conditions operators:
-
-| AWS conditions operator | FrostFS condition operator | Comment |
-|-----------------------------|-----------------------------|-------------------------------------------------------------------|
-| `StringEquals` | `StringEquals` | |
-| `StringNotEquals` | `StringNotEquals` | |
-| `StringEqualsIgnoreCase` | `StringEqualsIgnoreCase` | |
-| `StringNotEqualsIgnoreCase` | `StringNotEqualsIgnoreCase` | |
-| `StringLike` | `StringLike` | |
-| `StringNotLike` | `StringNotLike` | |
-| `NumericEquals` | `NumericEquals` | |
-| `NumericNotEquals` | `NumericNotEquals` | |
-| `NumericLessThan` | `NumericLessThan` | |
-| `NumericLessThanEquals` | `NumericLessThanEquals` | |
-| `NumericGreaterThan` | `NumericGreaterThan` | |
-| `NumericGreaterThanEquals` | `NumericGreaterThanEquals` | |
-| `DateEquals` | `StringEquals` | Date transforms to unix timestamp to be compared as string |
-| `DateNotEquals` | `StringNotEquals` | Date transforms to unix timestamp to be compared as string |
-| `DateLessThan` | `StringEqualsIgnoreCase` | Date transforms to unix timestamp to be compared as string |
-| `DateLessThanEquals` | `StringNotEqualsIgnoreCase` | Date transforms to unix timestamp to be compared as string |
-| `DateGreaterThan` | `StringLike` | Date transforms to unix timestamp to be compared as string |
-| `DateGreaterThanEquals` | `StringNotLike` | Date transforms to unix timestamp to be compared as string |
-| `Bool` | `StringEqualsIgnoreCase` | |
-| `IpAddress` | `IPAddress` | |
-| `NotIpAddress` | `NotIPAddress` | |
-| `ArnEquals` | `StringEquals` | |
-| `ArnLike` | `StringLike` | |
-| `ArnNotEquals` | `StringNotEquals` | |
-| `ArnNotLike` | `StringNotLike` | |
-| `SliceContains` | `SliceContains` | AWS spec doesn't contain such operator. This is FrostFS extension |
-
-For example, AWS conditions:
-
-```json
-{
- "Condition": {
- "ArnEquals": {"key16": ["val16"]},
- "ArnNotEquals": {"key18": ["val18"]},
- "ArnNotLike": {"key19": ["val19"]},
- "Bool": {"key13": ["True"]},
- "DateEquals": {"key7": ["2006-01-02T15:04:05+07:00"]},
- "DateGreaterThan": {"key11": ["2006-01-02T15:04:05-01:00"]},
- "DateGreaterThanEquals": {"key12": ["2006-01-02T15:04:05-03:00"]},
- "DateLessThan": {"key9": ["2006-01-02T15:04:05+06:00"]},
- "DateLessThanEquals": {"key10": ["2006-01-02T15:04:05+03:00"]},
- "DateNotEquals": {"key8": ["2006-01-02T15:04:05Z"]},
- "NumericEquals": {"key20": ["-20"]},
- "NumericGreaterThan": {"key24": ["-24.24"]},
- "NumericGreaterThanEquals": {"key25": ["+25.25"]},
- "NumericLessThan": {"key22": ["0"]},
- "NumericLessThanEquals": {"key23": ["23.23"]},
- "NumericNotEquals": {"key21": ["+21"]},
- "StringEquals": {"key1": ["val0"]},
- "StringEqualsIgnoreCase": {"key3": ["val3"]},
- "StringLike": {"key5": ["val5"]},
- "StringNotEquals": {"key2": ["val2"]},
- "StringNotEqualsIgnoreCase": {"key4": ["val4"]},
- "StringNotLike": {"key6": ["val6"]}
- }
-}
-```
-
-transforms to FrostFS conditions:
-
-```json
-{
- "Condition": [
- {"Op": "StringLike", "Object": "Request", "Key": "key5", "Value": "val5"},
- {"Op": "StringNotEquals", "Object": "Request", "Key": "key2", "Value": "val2"},
- {"Op": "StringGreaterThan", "Object": "Request", "Key": "key11", "Value": "1136217845"},
- {"Op": "StringGreaterThanEquals", "Object": "Request", "Key": "key12", "Value": "1136225045"},
- {"Op": "StringLessThan", "Object": "Request", "Key": "key9", "Value": "1136192645"},
- {"Op": "StringEqualsIgnoreCase", "Object": "Request", "Key": "key3", "Value": "val3"},
- {"Op": "StringEquals", "Object": "Request", "Key": "key16", "Value": "val16"},
- {"Op": "NumericLessThanEquals", "Object": "Request", "Key": "key23", "Value": "23.23"},
- {"Op": "StringNotEqualsIgnoreCase", "Object": "Request", "Key": "key4", "Value": "val4"},
- {"Op": "StringEquals", "Object": "Request", "Key": "key1", "Value": "val0"},
- {"Op": "StringLessThanEquals", "Object": "Request", "Key": "key10", "Value": "1136203445"},
- {"Op": "NumericGreaterThan", "Object": "Request", "Key": "key24", "Value": "-24.24"},
- {"Op": "NumericGreaterThanEquals", "Object": "Request", "Key": "key25", "Value": "+25.25"},
- {"Op": "NumericLessThan", "Object": "Request", "Key": "key22", "Value": "0"},
- {"Op": "StringNotEquals", "Object": "Request", "Key": "key8", "Value": "1136214245"},
- {"Op": "NumericEquals", "Object": "Request", "Key": "key20", "Value": "-20"},
- {"Op": "NumericNotEquals", "Object": "Request", "Key": "key21", "Value": "+21"},
- {"Op": "StringNotLike", "Object": "Request", "Key": "key6", "Value": "val6"},
- {"Op": "StringNotEquals", "Object": "Request", "Key": "key18", "Value": "val18"},
- {"Op": "StringNotLike", "Object": "Request", "Key": "key19", "Value": "val19"},
- {"Op": "StringEqualsIgnoreCase", "Object": "Request", "Key": "key13", "Value": "True"},
- {"Op": "StringEquals", "Object": "Request", "Key": "key7", "Value": "1136189045"}
- ]
-}
-```
-
-### S3
-
-#### Actions
-
-Each action allows some s3-gw methods, so we must transform action to specific method names
-(you can see exact mapping in table in [this file](../iam/converter_s3.go)).
-
-For example the following actions:
-
-```json
-{
- "Action": [
- "s3:DeleteObject",
- "iam:CreateUser"
- ]
-}
-```
-
-transforms to
-
-```json
-{
- "Actions": {
- "Inverted": false,
- "Names": [
- "s3:DeleteObject",
- "s3:DeleteMultipleObjects",
- "iam:CreateUser"
- ]
- }
-}
-```
-
-As we can see any `iam:*` action transformed as it is. But `s3:*` actions transforms according to
-[spec rules](https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazons3.html) and s3-gw
-[method names](https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/src/commit/2ab655b909c40db6f7a4e41e07d8b99167f791bd/api/middleware/constants.go#L3-L76).
-
-#### Resources
-
-Resource is transformed as it is:
-
-```json
-{
- "Resource": [
- "arn:aws:s3:::bucket/object"
- ]
-}
-```
-
-```json
-{
- "Resources": {
- "Inverted": false,
- "Names": [
- "arn:aws:s3:::bucket/object"
- ]
- }
-}
-```
-
-#### Principals
-
-To check user s3-gw uses special condition request property (`Owner`), so when AWS policy contains principal field
-it transforms to rule with appropriate condition. To get correct `Owner` property value special user resolver
-(`S3Resolver` interface in [converter_s3 file](../iam/converter_s3.go)) must be provided into convert function.
-
-For example such AWS json statement:
-
-```json
-{
- "Effect": "Allow",
- "Action": "*",
- "Resource": "*",
- "Principal": {
- "AWS": "arn:aws:iam::111122223333:user/JohnDoe"
- }
-}
-```
-
-transforms to the following FrostFS rule:
-
-```json
-{
- "Status": "Allow",
- "Actions": {
- "Inverted": false,
- "Names": [
- "*"
- ]
- },
- "Resources": {
- "Inverted": false,
- "Names": [
- "*"
- ]
- },
- "Any": false,
- "Condition": [
- {
- "Op": "StringEquals",
- "Object": "Request",
- "Key": "Owner",
- "Value": "NbUgTSFvPmsRxmGeWpuuGeJUoRoi6PErcM"
- }
- ]
-}
-```
-
-### Native
-
-#### Actions
-
-Each action allows some frostfs methods, so we must transform action to specific method names
-(you can see exact mapping in table in [this file](../iam/converter_native.go)).
-
-For example the following actions:
-
-```json
-{
- "Action": [
- "s3:DeleteObject",
- "iam:CreateUser"
- ]
-}
-```
-
-transforms to
-
-```json
-{
- "Actions": {
- "Inverted": false,
- "Names": [
- "PutObject",
- "HeadObject",
- "GetObject",
- "RangeObject",
- "GetContainer",
- "DeleteObject"
- ]
- }
-}
-```
-
-> **Note:** Only subset of s3:* actions can be transformed (exact value you can see in mapping table mentioned before).
-> If all provided actions is not applicable converter function returns appropriate error.
-
-Native methods (to which original actions are transformed) depend on which methods are invoked by appropriate s3-gw
-[method](https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/src/commit/2ab655b909c40db6f7a4e41e07d8b99167f791bd/api/middleware/constants.go#L3-L76).
-
-So in example above s3-gw during performing DeleteObject methods invokes the following methods:
-`["PutObject","HeadObject","GetObject","RangeObject","GetContainer","DeleteObject"]`
-
-#### Resources
-
-To transform resources the following is being performed:
-
-* Bucket name is resoled to container id (by providing `NativeResolver` interface implementation to converter)
-* Object name is transformed to condition with special `FilePath` attribute
- (that present on every object that was uploaded via s3-gw)
-
-For example, the following AWS policy statement:
-
-```json
-{
- "Principal": "*",
- "Effect": "Allow",
- "Action": "*",
- "Resource": "arn:aws:s3:::bucket/object"
-}
-```
-
-transforms to FrostFS native policy rule:
-
-```json
-{
- "Status": "Allow",
- "Actions": {
- "Inverted": false,
- "Names": [
- "*"
- ]
- },
- "Resources": {
- "Inverted": false,
- "Names": [
- "native:object//bucket/HFq67qbfhFEiEL7qDXqayo3F78yAvxXSXzwSa2hKM9bH/*",
- "native:container//bucket/HFq67qbfhFEiEL7qDXqayo3F78yAvxXSXzwSa2hKM9bH"
- ]
- },
- "Any": false,
- "Condition": [
- {
- "Op": "StringLike",
- "Object": "Resource",
- "Key": "FilePath",
- "Value": "object"
- }
- ]
-}
-```
-
-#### Principals
-
-To check user s3-gw uses special condition request property (`$Actor:publicKey`), so when AWS policy contains principal
-field it transforms to rule with appropriate condition. To get correct `$Actor:publicKey` property value
-special user resolver (`NativeResolver` interface in [converter_native file](../iam/converter_native.go)) must be
-provided into convert function.
-
-For example such AWS json statement:
-
-```json
-{
- "Effect": "Allow",
- "Action": "*",
- "Resource": "*",
- "Principal": {
- "AWS": "arn:aws:iam::111122223333:user/JohnDoe"
- }
-}
-```
-
-transforms to the following FrostFS rule:
-
-```json
-{
- "Status": "Allow",
- "Actions": {
- "Inverted": false,
- "Names": [
- "*"
- ]
- },
- "Resources": {
- "Inverted": false,
- "Names": [
- "native:object/*",
- "native:container/*"
- ]
- },
- "Any": false,
- "Condition": [
- {
- "Op": "StringEquals",
- "Object": "Request",
- "Key": "$Actor:publicKey",
- "Value": "031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a"
- }
- ]
-}
-```
diff --git a/docs/resource.md b/docs/resource.md
deleted file mode 100644
index 0d49cbc..0000000
--- a/docs/resource.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# 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.
diff --git a/go.mod b/go.mod
index 4fa2847..28836a0 100644
--- a/go.mod
+++ b/go.mod
@@ -1,35 +1,25 @@
module git.frostfs.info/TrueCloudLab/policy-engine
-go 1.22
+go 1.20
require (
- git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409111539-e7a05a49ff45
- github.com/google/uuid v1.3.1
- github.com/mailru/easyjson v0.7.7
- github.com/nspcc-dev/neo-go v0.105.0
+ git.frostfs.info/TrueCloudLab/frostfs-contract v0.18.1-0.20231129062201-a1b61d394958
+ github.com/nspcc-dev/neo-go v0.103.0
github.com/stretchr/testify v1.8.4
- golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
- github.com/golang/snappy v0.0.1 // indirect
+ github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/golang-lru v0.6.0 // indirect
- github.com/josharian/intern v1.0.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
- github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c // indirect
- github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231127165613-b35f351f0ba0 // indirect
+ github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 // indirect
+ github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5 // indirect
github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 // indirect
- github.com/twmb/murmur3 v1.1.5 // indirect
- go.etcd.io/bbolt v1.3.8 // indirect
- go.uber.org/multierr v1.10.0 // indirect
- go.uber.org/zap v1.26.0 // indirect
- golang.org/x/crypto v0.17.0 // indirect
+ golang.org/x/crypto v0.14.0 // indirect
golang.org/x/sync v0.3.0 // indirect
- golang.org/x/sys v0.15.0 // indirect
- golang.org/x/text v0.14.0 // indirect
+ golang.org/x/text v0.13.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 855cef3..0453bd3 100644
--- a/go.sum
+++ b/go.sum
@@ -1,166 +1,43 @@
-git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409111539-e7a05a49ff45 h1:Tp4I+XOLp3VCJORfxSamQtj3RZNISbaLM4WD5iIzXxg=
-git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409111539-e7a05a49ff45/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc=
-github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
-github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c=
-github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
-github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
-github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
-github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
-github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc=
-github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+git.frostfs.info/TrueCloudLab/frostfs-contract v0.18.1-0.20231129062201-a1b61d394958 h1:X9yPizADIhD3K/gdKVCthlAnf9aQ3UJJGnZgIwwixRQ=
+git.frostfs.info/TrueCloudLab/frostfs-contract v0.18.1-0.20231129062201-a1b61d394958/go.mod h1:rQWdsG18NaiFvkJpMguJev913KD/yleHaniRBkUyt0o=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
-github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
-github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4=
github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM=
-github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
-github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
-github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
-github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
-github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c h1:OOQeE613BH93ICPq3eke5N78gWNeMjcBWkmD2NKyXVg=
-github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
-github.com/nspcc-dev/neo-go v0.105.0 h1:vtNZYFEFySK8zRDhLzQYha849VzWrcKezlnq/oNQg/w=
-github.com/nspcc-dev/neo-go v0.105.0/go.mod h1:6pchIHg5okeZO955RxpTh5q0sUI0vtpgPM6Q+no1rlI=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231127165613-b35f351f0ba0 h1:N+dMIBmteXjJpkH6UZ7HmNftuFxkqszfGLbhsEctnv0=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231127165613-b35f351f0ba0/go.mod h1:J/Mk6+nKeKSW4wygkZQFLQ6SkLOSGX5Ga0RuuuktEag=
+github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 h1:n4ZaFCKt1pQJd7PXoMJabZWK9ejjbLOVrkl/lOUmshg=
+github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
+github.com/nspcc-dev/neo-go v0.103.0 h1:UVyWPhzZdfYFG35ORP3FRDLh8J/raRQ6m8SptDdlgfM=
+github.com/nspcc-dev/neo-go v0.103.0/go.mod h1:x+wmcYqpZYJwLp1l/pHZrqNp3RSWlkMymWGDij3/OPo=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5 h1:09CpI5uwsxb1EeFPIKQRwwWlfCmDD/Dwwh01lPiQScM=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5/go.mod h1:J/Mk6+nKeKSW4wygkZQFLQ6SkLOSGX5Ga0RuuuktEag=
github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE=
github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
-github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
-github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
-github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
-github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
-github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
-github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
-github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
-github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
-github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
-github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
-github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs=
-github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
-github.com/twmb/murmur3 v1.1.5 h1:i9OLS9fkuLzBXjt6dptlAEyk58fJsSTXbRg3SgVyqgk=
-github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
-github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
-github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 h1:JwtAtbp7r/7QSyGz8mKUbYJBg2+6Cd7OjM8o/GNOcVo=
github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1:RmMWU37GKR2s6pgrIEB4ixgpVCt/cf7dnJv3fuH1J1c=
-go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
-go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
-go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
-go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
-go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
-go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
-go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
-golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
-golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
-golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
-golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
-golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
+golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
+golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
-golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E=
-golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
+golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
-rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
diff --git a/iam/converter.go b/iam/converter.go
index 39d0819..b288de3 100644
--- a/iam/converter.go
+++ b/iam/converter.go
@@ -3,79 +3,15 @@ package iam
import (
"errors"
"fmt"
- "net/netip"
"strconv"
"strings"
"time"
"unicode/utf8"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
- "git.frostfs.info/TrueCloudLab/policy-engine/schema/common"
- "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
)
-const (
- s3ActionAbortMultipartUpload = "s3:AbortMultipartUpload"
- s3ActionCreateBucket = "s3:CreateBucket"
- s3ActionDeleteBucket = "s3:DeleteBucket"
- s3ActionDeleteBucketPolicy = "s3:DeleteBucketPolicy"
- s3ActionDeleteObject = "s3:DeleteObject"
- s3ActionDeleteObjectTagging = "s3:DeleteObjectTagging"
- s3ActionDeleteObjectVersion = "s3:DeleteObjectVersion"
- s3ActionDeleteObjectVersionTagging = "s3:DeleteObjectVersionTagging"
- s3ActionGetBucketACL = "s3:GetBucketAcl"
- s3ActionGetBucketCORS = "s3:GetBucketCORS"
- s3ActionGetBucketLocation = "s3:GetBucketLocation"
- s3ActionGetBucketNotification = "s3:GetBucketNotification"
- s3ActionGetBucketObjectLockConfiguration = "s3:GetBucketObjectLockConfiguration"
- s3ActionGetBucketPolicy = "s3:GetBucketPolicy"
- s3ActionGetBucketPolicyStatus = "s3:GetBucketPolicyStatus"
- s3ActionGetBucketTagging = "s3:GetBucketTagging"
- s3ActionGetBucketVersioning = "s3:GetBucketVersioning"
- s3ActionGetLifecycleConfiguration = "s3:GetLifecycleConfiguration"
- s3ActionGetObject = "s3:GetObject"
- s3ActionGetObjectACL = "s3:GetObjectAcl"
- s3ActionGetObjectAttributes = "s3:GetObjectAttributes"
- s3ActionGetObjectLegalHold = "s3:GetObjectLegalHold"
- s3ActionGetObjectRetention = "s3:GetObjectRetention"
- s3ActionGetObjectTagging = "s3:GetObjectTagging"
- s3ActionGetObjectVersion = "s3:GetObjectVersion"
- s3ActionGetObjectVersionACL = "s3:GetObjectVersionAcl"
- s3ActionGetObjectVersionAttributes = "s3:GetObjectVersionAttributes"
- s3ActionGetObjectVersionTagging = "s3:GetObjectVersionTagging"
- s3ActionListAllMyBuckets = "s3:ListAllMyBuckets"
- s3ActionListBucket = "s3:ListBucket"
- s3ActionListBucketMultipartUploads = "s3:ListBucketMultipartUploads"
- s3ActionListBucketVersions = "s3:ListBucketVersions"
- s3ActionListMultipartUploadParts = "s3:ListMultipartUploadParts"
- s3ActionPutBucketACL = "s3:PutBucketAcl"
- s3ActionPutBucketCORS = "s3:PutBucketCORS"
- s3ActionPutBucketNotification = "s3:PutBucketNotification"
- s3ActionPutBucketObjectLockConfiguration = "s3:PutBucketObjectLockConfiguration"
- s3ActionPutBucketPolicy = "s3:PutBucketPolicy"
- s3ActionPutBucketTagging = "s3:PutBucketTagging"
- s3ActionPutBucketVersioning = "s3:PutBucketVersioning"
- s3ActionPutLifecycleConfiguration = "s3:PutLifecycleConfiguration"
- s3ActionPutObject = "s3:PutObject"
- s3ActionPutObjectACL = "s3:PutObjectAcl"
- s3ActionPutObjectLegalHold = "s3:PutObjectLegalHold"
- s3ActionPutObjectRetention = "s3:PutObjectRetention"
- s3ActionPutObjectTagging = "s3:PutObjectTagging"
- s3ActionPutObjectVersionACL = "s3:PutObjectVersionAcl"
- s3ActionPutObjectVersionTagging = "s3:PutObjectVersionTagging"
- s3ActionPatchObject = "s3:PatchObject"
- s3ActionPutBucketPublicAccessBlock = "s3:PutBucketPublicAccessBlock"
- s3ActionGetBucketPublicAccessBlock = "s3:GetBucketPublicAccessBlock"
-)
-
-const (
- condKeyAWSPrincipalARN = "aws:PrincipalArn"
- condKeyAWSSourceIP = "aws:SourceIp"
- condKeyAWSPrincipalTagPrefix = "aws:PrincipalTag/"
- condKeyAWSRequestTagPrefix = "aws:RequestTag/"
- condKeyAWSResourceTagPrefix = "aws:ResourceTag/"
- userClaimTagPrefix = "tag-"
-)
+const condKeyAWSPrincipalARN = "aws:PrincipalArn"
const (
// String condition operators.
@@ -114,16 +50,12 @@ const (
CondArnLike string = "ArnLike"
CondArnNotEquals string = "ArnNotEquals"
CondArnNotLike string = "ArnNotLike"
-
- // Custom condition operators.
- CondSliceContains string = "SliceContains"
)
const (
arnIAMPrefix = "arn:aws:iam::"
s3ResourcePrefix = "arn:aws:s3:::"
s3ActionPrefix = "s3:"
- iamActionPrefix = "iam:"
)
var (
@@ -135,9 +67,6 @@ var (
// ErrInvalidActionFormat occurs when action has unknown/unsupported format.
ErrInvalidActionFormat = errors.New("invalid action format")
-
- // ErrActionsNotApplicable occurs when failed to convert any actions.
- ErrActionsNotApplicable = errors.New("actions not applicable")
)
type formPrincipalConditionFunc func(string) chain.Condition
@@ -186,10 +115,10 @@ func convertToChainCondition(c Conditions) ([]GroupedConditions, error) {
}
group.Conditions[i] = chain.Condition{
- Op: condType,
- Kind: chain.KindRequest,
- Key: transformKey(key),
- Value: converted,
+ Op: condType,
+ Object: chain.ObjectRequest,
+ Key: key,
+ Value: converted,
}
}
grouped = append(grouped, group)
@@ -199,20 +128,6 @@ func convertToChainCondition(c Conditions) ([]GroupedConditions, error) {
return grouped, nil
}
-func transformKey(key string) string {
- tagName, isTag := strings.CutPrefix(key, condKeyAWSPrincipalTagPrefix)
- if isTag {
- return fmt.Sprintf(common.PropertyKeyFormatFrostFSIDUserClaim, userClaimTagPrefix+tagName)
- }
-
- switch key {
- case condKeyAWSSourceIP:
- return common.PropertyKeyFrostFSSourceIP
- }
-
- return key
-}
-
func getConditionTypeAndConverter(op string) (chain.ConditionType, convertFunction, error) {
switch {
case strings.HasPrefix(op, "String"):
@@ -246,7 +161,8 @@ func getConditionTypeAndConverter(op string) (chain.ConditionType, convertFuncti
return 0, nil, fmt.Errorf("unsupported condition operator: '%s'", op)
}
case strings.HasPrefix(op, "Numeric"):
- return numericConditionTypeAndConverter(op)
+ // TODO
+ return 0, nil, fmt.Errorf("currently nummeric conditions unsupported: '%s'", op)
case strings.HasPrefix(op, "Date"):
switch op {
case CondDateEquals:
@@ -267,30 +183,13 @@ func getConditionTypeAndConverter(op string) (chain.ConditionType, convertFuncti
case op == CondBool:
return chain.CondStringEqualsIgnoreCase, noConvertFunction, nil
case op == CondIPAddress:
- return chain.CondIPAddress, ipConvertFunction, nil
+ // todo consider using converters
+ // "203.0.113.0/24" -> "203.0.113.*",
+ // "2001:DB8:1234:5678::/64" -> "2001:DB8:1234:5678:*"
+ // or having specific condition type for IP
+ return chain.CondStringLike, noConvertFunction, nil
case op == CondNotIPAddress:
- return chain.CondNotIPAddress, ipConvertFunction, nil
- case op == CondSliceContains:
- return chain.CondSliceContains, noConvertFunction, nil
- default:
- return 0, nil, fmt.Errorf("unsupported condition operator: '%s'", op)
- }
-}
-
-func numericConditionTypeAndConverter(op string) (chain.ConditionType, convertFunction, error) {
- switch op {
- case CondNumericEquals:
- return chain.CondNumericEquals, numericConvertFunction, nil
- case CondNumericNotEquals:
- return chain.CondNumericNotEquals, numericConvertFunction, nil
- case CondNumericLessThan:
- return chain.CondNumericLessThan, numericConvertFunction, nil
- case CondNumericLessThanEquals:
- return chain.CondNumericLessThanEquals, numericConvertFunction, nil
- case CondNumericGreaterThan:
- return chain.CondNumericGreaterThan, numericConvertFunction, nil
- case CondNumericGreaterThanEquals:
- return chain.CondNumericGreaterThanEquals, numericConvertFunction, nil
+ return chain.CondStringNotLike, noConvertFunction, nil
default:
return 0, nil, fmt.Errorf("unsupported condition operator: '%s'", op)
}
@@ -302,25 +201,6 @@ func noConvertFunction(val string) (string, error) {
return val, nil
}
-func numericConvertFunction(val string) (string, error) {
- if _, err := fixedn.Fixed8FromString(val); err == nil {
- return val, nil
- }
-
- return "", fmt.Errorf("invalid numeric value: '%s'", val)
-}
-
-func ipConvertFunction(val string) (string, error) {
- if _, err := netip.ParsePrefix(val); err != nil {
- if _, err = netip.ParseAddr(val); err != nil {
- return "", err
- }
- val += "/32"
- }
-
- return val, nil
-}
-
func dateConvertFunction(val string) (string, error) {
if _, err := strconv.ParseInt(val, 10, 64); err == nil {
return val, nil
@@ -363,35 +243,53 @@ func parsePrincipalAsIAMUser(principal string) (account string, user string, err
return account, user, nil
}
-func validateResource(resource string) error {
+func parseResourceAsS3ARN(resource string) (bucket string, object string, err error) {
if resource == Wildcard {
- return nil
+ return Wildcard, Wildcard, nil
}
- if !strings.HasPrefix(resource, s3ResourcePrefix) && !strings.HasPrefix(resource, arnIAMPrefix) {
- return ErrInvalidResourceFormat
+ if !strings.HasPrefix(resource, s3ResourcePrefix) {
+ return "", "", ErrInvalidResourceFormat
}
- index := strings.IndexByte(resource, Wildcard[0])
- if index != -1 && index != utf8.RuneCountInString(resource)-1 {
- return ErrInvalidResourceFormat
+ // iam arn format arn:aws:s3:::/
+ s3Resource := strings.TrimPrefix(resource, s3ResourcePrefix)
+ sepIndex := strings.Index(s3Resource, "/")
+ if sepIndex < 0 {
+ return s3Resource, Wildcard, nil
}
- return nil
+ bucket = s3Resource[:sepIndex]
+ object = s3Resource[sepIndex+1:]
+ if len(object) == 0 {
+ return bucket, Wildcard, nil
+ }
+
+ if bucket == Wildcard && object != Wildcard {
+ return "", "", ErrInvalidResourceFormat
+ }
+
+ return bucket, object, nil
}
-func validateAction(action string) (bool, error) {
- isIAM := strings.HasPrefix(action, iamActionPrefix)
- if !strings.HasPrefix(action, s3ActionPrefix) && !isIAM {
- return false, ErrInvalidActionFormat
+func parseActionAsS3Action(action string) (string, error) {
+ if action == Wildcard {
+ return Wildcard, nil
}
- index := strings.IndexByte(action, Wildcard[0])
- if index != -1 && index != utf8.RuneCountInString(action)-1 {
- return false, ErrInvalidActionFormat
+ if !strings.HasPrefix(action, s3ActionPrefix) {
+ return "", ErrInvalidActionFormat
}
- return isIAM, nil
+ // iam arn format :s3:
+ s3Action := strings.TrimPrefix(action, s3ActionPrefix)
+
+ index := strings.IndexByte(s3Action, Wildcard[0])
+ if index != -1 && index != utf8.RuneCountInString(s3Action)-1 {
+ return "", ErrInvalidActionFormat
+ }
+
+ return s3Action, nil
}
func splitGroupedConditions(groupedConditions []GroupedConditions) [][]chain.Condition {
diff --git a/iam/converter_native.go b/iam/converter_native.go
index 40373f4..4abfcdf 100644
--- a/iam/converter_native.go
+++ b/iam/converter_native.go
@@ -3,7 +3,6 @@ package iam
import (
"errors"
"fmt"
- "strings"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
@@ -11,89 +10,28 @@ import (
const PropertyKeyFilePath = "FilePath"
-var actionToNativeOpMap = map[string][]string{
- s3ActionAbortMultipartUpload: {native.MethodGetContainer, native.MethodDeleteObject, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
- s3ActionCreateBucket: {native.MethodGetContainer, native.MethodPutContainer, native.MethodSetContainerEACL, native.MethodGetObject, native.MethodPutObject},
- s3ActionDeleteBucket: {native.MethodGetContainer, native.MethodDeleteContainer, native.MethodSearchObject, native.MethodHeadObject, native.MethodGetObject},
- s3ActionDeleteBucketPolicy: {native.MethodGetContainer},
- s3ActionDeleteObject: {native.MethodGetContainer, native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject},
- s3ActionDeleteObjectTagging: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
- s3ActionDeleteObjectVersion: {native.MethodGetContainer, native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject},
- s3ActionDeleteObjectVersionTagging: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
- s3ActionGetBucketACL: {native.MethodGetContainer, native.MethodGetContainerEACL, native.MethodGetObject},
- s3ActionGetBucketCORS: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject},
- s3ActionGetBucketLocation: {native.MethodGetContainer},
- s3ActionGetBucketNotification: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject},
- s3ActionGetBucketObjectLockConfiguration: {native.MethodGetContainer, native.MethodGetObject},
- s3ActionGetBucketPolicy: {native.MethodGetContainer},
- s3ActionGetBucketPolicyStatus: {native.MethodGetContainer},
- s3ActionGetBucketTagging: {native.MethodGetContainer, native.MethodGetObject},
- s3ActionGetBucketVersioning: {native.MethodGetContainer, native.MethodGetObject},
- s3ActionGetLifecycleConfiguration: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject},
- s3ActionGetObject: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject},
- s3ActionGetObjectACL: {native.MethodGetContainer, native.MethodGetContainerEACL, native.MethodGetObject, native.MethodHeadObject},
- s3ActionGetObjectAttributes: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject},
- s3ActionGetObjectLegalHold: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject},
- s3ActionGetObjectRetention: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject},
- s3ActionGetObjectTagging: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject},
- s3ActionGetObjectVersion: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject},
- s3ActionGetObjectVersionACL: {native.MethodGetContainer, native.MethodGetContainerEACL, native.MethodGetObject, native.MethodHeadObject},
- s3ActionGetObjectVersionAttributes: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject},
- s3ActionGetObjectVersionTagging: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject},
- s3ActionListAllMyBuckets: {native.MethodListContainers, native.MethodGetContainer},
- s3ActionListBucket: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject},
- s3ActionListBucketMultipartUploads: {native.MethodGetContainer, native.MethodGetObject},
- s3ActionListBucketVersions: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject},
- s3ActionListMultipartUploadParts: {native.MethodGetContainer, native.MethodGetObject},
- s3ActionPutBucketACL: {native.MethodGetContainer, native.MethodSetContainerEACL, native.MethodGetObject, native.MethodPutObject},
- s3ActionPutBucketCORS: {native.MethodGetContainer, native.MethodGetObject, native.MethodPutObject},
- s3ActionPutBucketNotification: {native.MethodGetContainer, native.MethodHeadObject, native.MethodDeleteObject, native.MethodGetObject, native.MethodPutObject},
- s3ActionPutBucketObjectLockConfiguration: {native.MethodGetContainer, native.MethodGetObject, native.MethodPutObject},
- s3ActionPutBucketPolicy: {native.MethodGetContainer},
- s3ActionPutBucketTagging: {native.MethodGetContainer, native.MethodGetObject, native.MethodPutObject},
- s3ActionPutBucketVersioning: {native.MethodGetContainer, native.MethodGetObject, native.MethodPutObject},
- s3ActionPutLifecycleConfiguration: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodPutObject, native.MethodDeleteObject},
- s3ActionPutObject: {native.MethodGetContainer, native.MethodPutObject, native.MethodGetObject, native.MethodHeadObject, native.MethodRangeObject},
- s3ActionPutObjectACL: {native.MethodGetContainer, native.MethodGetContainerEACL, native.MethodSetContainerEACL, native.MethodGetObject, native.MethodHeadObject},
- s3ActionPutObjectLegalHold: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
- s3ActionPutObjectRetention: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
- s3ActionPutObjectTagging: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
- s3ActionPutObjectVersionACL: {native.MethodGetContainer, native.MethodGetContainerEACL, native.MethodSetContainerEACL, native.MethodGetObject, native.MethodHeadObject},
- s3ActionPutObjectVersionTagging: {native.MethodGetContainer, native.MethodHeadObject, native.MethodGetObject, native.MethodPutObject},
- s3ActionPatchObject: {native.MethodGetContainer, native.MethodGetObject, native.MethodHeadObject, native.MethodPatchObject, native.MethodPutObject, native.MethodRangeObject},
- s3ActionPutBucketPublicAccessBlock: {native.MethodGetContainer, native.MethodPutObject, native.MethodDeleteObject, native.MethodGetObject},
- s3ActionGetBucketPublicAccessBlock: {native.MethodGetContainer, native.MethodGetObject},
+// ErrActionsNotApplicable occurs when failed to convert any actions.
+var ErrActionsNotApplicable = errors.New("actions not applicable")
+
+var actionToOpMap = map[string][]string{
+ supportedS3ActionDeleteObject: {native.MethodDeleteObject},
+ supportedS3ActionGetObject: {native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject},
+ supportedS3ActionHeadObject: {native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject},
+ supportedS3ActionPutObject: {native.MethodPutObject},
+ supportedS3ActionListBucket: {native.MethodGetObject, native.MethodHeadObject, native.MethodSearchObject, native.MethodRangeObject, native.MethodHashObject},
}
-var containerNativeOperations = map[string]struct{}{
- native.MethodPutContainer: {},
- native.MethodDeleteContainer: {},
- native.MethodGetContainer: {},
- native.MethodListContainers: {},
- native.MethodSetContainerEACL: {},
- native.MethodGetContainerEACL: {},
-}
-
-var objectNativeOperations = map[string]struct{}{
- native.MethodGetObject: {},
- native.MethodPutObject: {},
- native.MethodHeadObject: {},
- native.MethodDeleteObject: {},
- native.MethodSearchObject: {},
- native.MethodRangeObject: {},
- native.MethodHashObject: {},
-}
-
-var errConditionKeyNotApplicable = errors.New("condition key is not applicable")
+const (
+ supportedS3ActionDeleteObject = "DeleteObject"
+ supportedS3ActionGetObject = "GetObject"
+ supportedS3ActionHeadObject = "HeadObject"
+ supportedS3ActionPutObject = "PutObject"
+ supportedS3ActionListBucket = "ListBucket"
+)
type NativeResolver interface {
GetUserKey(account, name string) (string, error)
- GetBucketInfo(bucket string) (*BucketInfo, error)
-}
-
-type BucketInfo struct {
- Namespace string
- Container string
+ GetBucketCID(bucket string) (string, error)
}
func ConvertToNativeChain(p Policy, resolver NativeResolver) (*chain.Chain, error) {
@@ -105,11 +43,6 @@ func ConvertToNativeChain(p Policy, resolver NativeResolver) (*chain.Chain, erro
for _, statement := range p.Statement {
status := formStatus(statement)
- if status != chain.Allow {
- // Most s3 methods share the same native operations. Deny rules must not affect shared native operations,
- // therefore this code skips all deny rules for native protocol. Deny is applied for s3 protocol only, in this case.
- continue
- }
action, actionInverted := statement.action()
nativeActions, err := formNativeActionNames(action)
@@ -122,16 +55,13 @@ func ConvertToNativeChain(p Policy, resolver NativeResolver) (*chain.Chain, erro
}
resource, resourceInverted := statement.resource()
- groupedResources, err := formNativeResourceNamesAndConditions(resource, resolver, getActionTypes(nativeActions))
+ groupedResources, err := formNativeResourceNamesAndConditions(resource, resolver)
if err != nil {
return nil, err
}
groupedConditions, err := convertToNativeChainCondition(statement.Conditions, resolver)
if err != nil {
- if errors.Is(err, errConditionKeyNotApplicable) {
- continue
- }
return nil, err
}
splitConditions := splitGroupedConditions(groupedConditions)
@@ -144,12 +74,7 @@ func ConvertToNativeChain(p Policy, resolver NativeResolver) (*chain.Chain, erro
for _, groupedResource := range groupedResources {
for _, principal := range principals {
for _, conditions := range splitConditions {
- var principalCondition []chain.Condition
- if principal != Wildcard {
- principalCondition = []chain.Condition{principalCondFn(principal)}
- }
-
- ruleConditions := append(principalCondition, groupedResource.Conditions...)
+ ruleConditions := append([]chain.Condition{principalCondFn(principal)}, groupedResource.Conditions...)
r := chain.Rule{
Status: status,
@@ -173,23 +98,6 @@ func ConvertToNativeChain(p Policy, resolver NativeResolver) (*chain.Chain, erro
return &engineChain, nil
}
-func getActionTypes(nativeActions []string) ActionTypes {
- var res ActionTypes
- for _, action := range nativeActions {
- if res.Object && res.Container {
- break
- }
-
- _, isObj := objectNativeOperations[action]
- _, isCnr := containerNativeOperations[action]
-
- res.Object = res.Object || isObj || action == Wildcard
- res.Container = res.Container || isCnr || action == Wildcard
- }
-
- return res
-}
-
func getNativePrincipalsAndConditionFunc(statement Statement, resolver NativeResolver) ([]string, formPrincipalConditionFunc, error) {
var principals []string
var op chain.ConditionType
@@ -217,43 +125,28 @@ func getNativePrincipalsAndConditionFunc(statement Statement, resolver NativeRes
return principals, func(principal string) chain.Condition {
return chain.Condition{
- Op: op,
- Kind: chain.KindRequest,
- Key: native.PropertyKeyActorPublicKey,
- Value: principal,
+ Op: op,
+ Object: chain.ObjectRequest,
+ Key: native.PropertyKeyActorPublicKey,
+ Value: principal,
}
}, nil
}
func convertToNativeChainCondition(c Conditions, resolver NativeResolver) ([]GroupedConditions, error) {
return convertToChainConditions(c, func(gr GroupedConditions) (GroupedConditions, error) {
- res := GroupedConditions{
- Conditions: make([]chain.Condition, 0, len(gr.Conditions)),
- Any: gr.Any,
- }
-
for i := range gr.Conditions {
- switch {
- case gr.Conditions[i].Key == condKeyAWSMFAPresent:
- return GroupedConditions{}, errConditionKeyNotApplicable
- case gr.Conditions[i].Key == condKeyAWSPrincipalARN:
+ if gr.Conditions[i].Key == condKeyAWSPrincipalARN {
gr.Conditions[i].Key = native.PropertyKeyActorPublicKey
val, err := formPrincipalKey(gr.Conditions[i].Value, resolver)
if err != nil {
return GroupedConditions{}, err
}
gr.Conditions[i].Value = val
- res.Conditions = append(res.Conditions, gr.Conditions[i])
- case strings.HasPrefix(gr.Conditions[i].Key, condKeyAWSRequestTagPrefix) ||
- strings.HasPrefix(gr.Conditions[i].Key, condKeyAWSResourceTagPrefix):
- // Tags exist only in S3 requests, so native protocol should not process such conditions.
- continue
- default:
- res.Conditions = append(res.Conditions, gr.Conditions[i])
}
}
- return res, nil
+ return gr, nil
})
}
@@ -262,106 +155,53 @@ type GroupedResources struct {
Conditions []chain.Condition
}
-type ActionTypes struct {
- Object bool
- Container bool
-}
-
-func formNativeResourceNamesAndConditions(names []string, resolver NativeResolver, actionTypes ActionTypes) ([]GroupedResources, error) {
- if !actionTypes.Object && !actionTypes.Container {
- return nil, ErrActionsNotApplicable
- }
-
+func formNativeResourceNamesAndConditions(names []string, resolver NativeResolver) ([]GroupedResources, error) {
res := make([]GroupedResources, 0, len(names))
- combined := make(map[string]struct{})
+ var combined []string
- for _, resource := range names {
- if err := validateResource(resource); err != nil {
- return nil, err
- }
-
- if resource == Wildcard {
- res = res[:0]
- return append(res, formWildcardNativeResource(actionTypes)), nil
- }
-
- if !strings.HasPrefix(resource, s3ResourcePrefix) {
- continue
- }
-
- var bkt, obj string
- s3Resource := strings.TrimPrefix(resource, s3ResourcePrefix)
- if s3Resource == Wildcard {
- res = res[:0]
- return append(res, formWildcardNativeResource(actionTypes)), nil
- }
-
- if sepIndex := strings.Index(s3Resource, "/"); sepIndex < 0 {
- bkt = s3Resource
- } else {
- bkt = s3Resource[:sepIndex]
- obj = s3Resource[sepIndex+1:]
- if len(obj) == 0 {
- obj = Wildcard
- }
- }
-
- bktInfo, err := resolver.GetBucketInfo(bkt)
+ for i := range names {
+ bkt, obj, err := parseResourceAsS3ARN(names[i])
if err != nil {
return nil, err
}
- if obj == Wildcard && actionTypes.Object { // this corresponds to arn:aws:s3:::BUCKET/ or arn:aws:s3:::BUCKET/*
- combined[fmt.Sprintf(native.ResourceFormatNamespaceContainerObjects, bktInfo.Namespace, bktInfo.Container)] = struct{}{}
- combined[fmt.Sprintf(native.ResourceFormatNamespaceContainer, bktInfo.Namespace, bktInfo.Container)] = struct{}{}
- continue
+ if bkt == Wildcard {
+ res = res[:0]
+ return append(res, GroupedResources{Names: []string{native.ResourceFormatAllObjects}}), nil
}
- if obj == "" && actionTypes.Container { // this corresponds to arn:aws:s3:::BUCKET
- combined[fmt.Sprintf(native.ResourceFormatNamespaceContainer, bktInfo.Namespace, bktInfo.Container)] = struct{}{}
+
+ cnrID, err := resolver.GetBucketCID(bkt)
+ if err != nil {
+ return nil, err
+ }
+ resource := fmt.Sprintf(native.ResourceFormatRootContainerObjects, cnrID)
+
+ if obj == Wildcard {
+ combined = append(combined, resource)
continue
}
res = append(res, GroupedResources{
- Names: []string{
- fmt.Sprintf(native.ResourceFormatNamespaceContainerObjects, bktInfo.Namespace, bktInfo.Container),
- fmt.Sprintf(native.ResourceFormatNamespaceContainer, bktInfo.Namespace, bktInfo.Container),
- },
+ Names: []string{resource},
Conditions: []chain.Condition{
{
- Op: chain.CondStringLike,
- Kind: chain.KindResource,
- Key: PropertyKeyFilePath,
- Value: obj,
+ Op: chain.CondStringLike,
+ Object: chain.ObjectResource,
+ Key: PropertyKeyFilePath,
+ Value: obj,
},
},
})
}
if len(combined) != 0 {
- gr := GroupedResources{Names: make([]string, 0, len(combined))}
- for key := range combined {
- gr.Names = append(gr.Names, key)
- }
-
- res = append(res, gr)
+ res = append(res, GroupedResources{Names: combined})
}
return res, nil
}
-func formWildcardNativeResource(actionTypes ActionTypes) GroupedResources {
- groupedNames := make([]string, 0, 2)
- if actionTypes.Object {
- groupedNames = append(groupedNames, native.ResourceFormatAllObjects)
- }
- if actionTypes.Container {
- groupedNames = append(groupedNames, native.ResourceFormatAllContainers)
- }
-
- return GroupedResources{Names: groupedNames}
-}
-
func formNativePrincipal(principal []string, resolver NativeResolver) ([]string, error) {
res := make([]string, len(principal))
@@ -390,39 +230,17 @@ func formPrincipalKey(principal string, resolver NativeResolver) (string, error)
}
func formNativeActionNames(names []string) ([]string, error) {
- uniqueActions := make(map[string]struct{}, len(names))
+ res := make([]string, 0, len(names))
- for _, action := range names {
- if action == Wildcard {
- return []string{Wildcard}, nil
- }
-
- isIAM, err := validateAction(action)
+ for i := range names {
+ action, err := parseActionAsS3Action(names[i])
if err != nil {
return nil, err
}
-
- if isIAM {
- continue
- }
-
- if action[len(s3ActionPrefix):] == Wildcard {
+ if action == Wildcard {
return []string{Wildcard}, nil
}
-
- nativeActions := actionToNativeOpMap[action]
- if len(nativeActions) == 0 {
- return nil, ErrActionsNotApplicable
- }
-
- for _, nativeAction := range nativeActions {
- uniqueActions[nativeAction] = struct{}{}
- }
- }
-
- res := make([]string, 0, len(uniqueActions))
- for key := range uniqueActions {
- res = append(res, key)
+ res = append(res, actionToOpMap[action]...)
}
return res, nil
diff --git a/iam/converter_s3.go b/iam/converter_s3.go
index 7aa3190..094c3d3 100644
--- a/iam/converter_s3.go
+++ b/iam/converter_s3.go
@@ -2,73 +2,11 @@ package iam
import (
"fmt"
- "strings"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/s3"
)
-const condKeyAWSMFAPresent = "aws:MultiFactorAuthPresent"
-
-var actionToS3OpMap = map[string][]string{
- s3ActionAbortMultipartUpload: {s3ActionAbortMultipartUpload},
- s3ActionCreateBucket: {s3ActionCreateBucket},
- s3ActionDeleteBucket: {s3ActionDeleteBucket},
- s3ActionDeleteBucketPolicy: {s3ActionDeleteBucketPolicy},
- s3ActionDeleteObjectTagging: {s3ActionDeleteObjectTagging},
- s3ActionGetBucketLocation: {s3ActionGetBucketLocation},
- s3ActionGetBucketNotification: {s3ActionGetBucketNotification},
- s3ActionGetBucketPolicy: {s3ActionGetBucketPolicy},
- s3ActionGetBucketPolicyStatus: {s3ActionGetBucketPolicyStatus},
- s3ActionGetBucketTagging: {s3ActionGetBucketTagging},
- s3ActionGetBucketVersioning: {s3ActionGetBucketVersioning},
- s3ActionGetObjectAttributes: {s3ActionGetObjectAttributes},
- s3ActionGetObjectLegalHold: {s3ActionGetObjectLegalHold},
- s3ActionGetObjectRetention: {s3ActionGetObjectRetention},
- s3ActionGetObjectTagging: {s3ActionGetObjectTagging},
- s3ActionPutBucketNotification: {s3ActionPutBucketNotification},
- s3ActionPutBucketPolicy: {s3ActionPutBucketPolicy},
- s3ActionPutBucketVersioning: {s3ActionPutBucketVersioning},
- s3ActionPutObjectLegalHold: {s3ActionPutObjectLegalHold},
- s3ActionPutObjectRetention: {s3ActionPutObjectRetention},
- s3ActionPutObjectTagging: {s3ActionPutObjectTagging},
- s3ActionPatchObject: {s3ActionPatchObject},
-
- s3ActionListAllMyBuckets: {"s3:ListBuckets"},
- s3ActionListBucket: {"s3:HeadBucket", "s3:GetBucketLocation", "s3:ListObjectsV1", "s3:ListObjectsV2"},
- s3ActionListBucketVersions: {"s3:ListBucketObjectVersions"},
- s3ActionListBucketMultipartUploads: {"s3:ListMultipartUploads"},
- s3ActionGetBucketObjectLockConfiguration: {"s3:GetBucketObjectLockConfig"},
- s3ActionGetLifecycleConfiguration: {"s3:GetBucketLifecycle"},
- s3ActionGetBucketACL: {"s3:GetBucketACL"},
- s3ActionGetBucketCORS: {"s3:GetBucketCors"},
- s3ActionPutBucketTagging: {"s3:PutBucketTagging", "s3:DeleteBucketTagging"},
- s3ActionPutBucketObjectLockConfiguration: {"s3:PutBucketObjectLockConfig"},
- s3ActionPutLifecycleConfiguration: {"s3:PutBucketLifecycle", "s3:DeleteBucketLifecycle"},
- s3ActionPutBucketACL: {"s3:PutBucketACL"},
- s3ActionPutBucketCORS: {"s3:PutBucketCors", "s3:DeleteBucketCors"},
-
- s3ActionListMultipartUploadParts: {"s3:ListParts"},
- s3ActionGetObjectACL: {"s3:GetObjectACL"},
- s3ActionGetObject: {"s3:GetObject", "s3:HeadObject"},
- s3ActionGetObjectVersion: {"s3:GetObject", "s3:HeadObject"},
- s3ActionGetObjectVersionACL: {"s3:GetObjectACL"},
- s3ActionGetObjectVersionAttributes: {"s3:GetObjectAttributes"},
- s3ActionGetObjectVersionTagging: {"s3:GetObjectTagging"},
- s3ActionPutObjectACL: {"s3:PutObjectACL"},
- s3ActionPutObjectVersionACL: {"s3:PutObjectACL"},
- s3ActionPutObjectVersionTagging: {"s3:PutObjectTagging"},
- s3ActionPutObject: {
- "s3:PutObject", "s3:PostObject", "s3:CopyObject",
- "s3:UploadPart", "s3:UploadPartCopy", "s3:CreateMultipartUpload", "s3:CompleteMultipartUpload",
- },
- s3ActionDeleteObjectVersionTagging: {"s3:DeleteObjectTagging"},
- s3ActionDeleteObject: {"s3:DeleteObject", "s3:DeleteMultipleObjects"},
- s3ActionDeleteObjectVersion: {"s3:DeleteObject", "s3:DeleteMultipleObjects"},
- s3ActionPutBucketPublicAccessBlock: {"s3:PutPublicAccessBlock", "s3:DeletePublicAccessBlock"},
- s3ActionGetBucketPublicAccessBlock: {"s3:GetPublicAccessBlock"},
-}
-
type S3Resolver interface {
GetUserAddress(account, user string) (string, error)
}
@@ -83,21 +21,19 @@ func ConvertToS3Chain(p Policy, resolver S3Resolver) (*chain.Chain, error) {
for _, statement := range p.Statement {
status := formStatus(statement)
- actions, actionInverted := statement.action()
- s3Actions, err := formS3ActionNames(actions)
+ action, actionInverted := statement.action()
+ s3Actions, err := formS3ActionNames(action)
if err != nil {
return nil, err
}
ruleAction := chain.Actions{Inverted: actionInverted, Names: s3Actions}
- if len(ruleAction.Names) == 0 {
- continue
- }
- resources, resourceInverted := statement.resource()
- if err := validateS3ResourceNames(resources); err != nil {
+ resource, resourceInverted := statement.resource()
+ s3Resources, err := formS3ResourceNames(resource)
+ if err != nil {
return nil, err
}
- ruleResource := chain.Resources{Inverted: resourceInverted, Names: resources}
+ ruleResource := chain.Resources{Inverted: resourceInverted, Names: s3Resources}
groupedConditions, err := convertToS3ChainCondition(statement.Conditions, resolver)
if err != nil {
@@ -112,26 +48,17 @@ func ConvertToS3Chain(p Policy, resolver S3Resolver) (*chain.Chain, error) {
for _, principal := range principals {
for _, conditions := range splitConditions {
- var principalCondition []chain.Condition
- if principal != Wildcard {
- principalCondition = []chain.Condition{principalCondFn(principal)}
- }
-
r := chain.Rule{
Status: status,
Actions: ruleAction,
Resources: ruleResource,
- Condition: append(principalCondition, conditions...),
+ Condition: append([]chain.Condition{principalCondFn(principal)}, conditions...),
}
engineChain.Rules = append(engineChain.Rules, r)
}
}
}
- if len(engineChain.Rules) == 0 {
- return nil, ErrActionsNotApplicable
- }
-
return &engineChain, nil
}
@@ -162,10 +89,10 @@ func getS3PrincipalsAndConditionFunc(statement Statement, resolver S3Resolver) (
return principals, func(principal string) chain.Condition {
return chain.Condition{
- Op: op,
- Kind: chain.KindRequest,
- Key: s3.PropertyKeyOwner,
- Value: principal,
+ Op: op,
+ Object: chain.ObjectRequest,
+ Key: s3.PropertyKeyOwner,
+ Value: principal,
}
}, nil
}
@@ -173,19 +100,13 @@ func getS3PrincipalsAndConditionFunc(statement Statement, resolver S3Resolver) (
func convertToS3ChainCondition(c Conditions, resolver S3Resolver) ([]GroupedConditions, error) {
return convertToChainConditions(c, func(gr GroupedConditions) (GroupedConditions, error) {
for i := range gr.Conditions {
- switch {
- case gr.Conditions[i].Key == condKeyAWSPrincipalARN:
+ if gr.Conditions[i].Key == condKeyAWSPrincipalARN {
gr.Conditions[i].Key = s3.PropertyKeyOwner
val, err := formPrincipalOwner(gr.Conditions[i].Value, resolver)
if err != nil {
return GroupedConditions{}, err
}
gr.Conditions[i].Value = val
-
- case gr.Conditions[i].Key == condKeyAWSMFAPresent:
- gr.Conditions[i].Key = s3.PropertyKeyAccessBoxAttrMFA
- case strings.HasPrefix(gr.Conditions[i].Key, condKeyAWSResourceTagPrefix):
- gr.Conditions[i].Kind = chain.KindResource
}
}
@@ -220,52 +141,32 @@ func formPrincipalOwner(principal string, resolver S3Resolver) (string, error) {
return address, nil
}
-func validateS3ResourceNames(names []string) error {
+func formS3ResourceNames(names []string) ([]string, error) {
+ res := make([]string, len(names))
for i := range names {
- if err := validateResource(names[i]); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func formS3ActionNames(names []string) ([]string, error) {
- uniqueActions := make(map[string]struct{}, len(names))
-
- for _, action := range names {
- if action == Wildcard {
- return []string{Wildcard}, nil
- }
-
- isIAM, err := validateAction(action)
+ bkt, obj, err := parseResourceAsS3ARN(names[i])
if err != nil {
return nil, err
}
- if isIAM {
- uniqueActions[action] = struct{}{}
+ if bkt == Wildcard {
+ res[i] = bkt
continue
}
- if action[len(s3ActionPrefix):] == Wildcard {
- uniqueActions[action] = struct{}{}
- continue
- }
-
- s3Actions := actionToS3OpMap[action]
- if len(s3Actions) == 0 {
- return nil, ErrActionsNotApplicable
- }
-
- for _, s3Action := range s3Actions {
- uniqueActions[s3Action] = struct{}{}
- }
- }
-
- res := make([]string, 0, len(uniqueActions))
- for key := range uniqueActions {
- res = append(res, key)
+ res[i] = bkt + "/" + obj
+ }
+
+ return res, nil
+}
+
+func formS3ActionNames(names []string) ([]string, error) {
+ var err error
+ res := make([]string, len(names))
+ for i := range names {
+ if res[i], err = parseActionAsS3Action(names[i]); err != nil {
+ return nil, err
+ }
}
return res, nil
diff --git a/iam/converter_test.go b/iam/converter_test.go
index 55787fd..7ec177d 100644
--- a/iam/converter_test.go
+++ b/iam/converter_test.go
@@ -11,30 +11,28 @@ import (
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine/inmemory"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource/testutil"
- "git.frostfs.info/TrueCloudLab/policy-engine/schema/common"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/s3"
"github.com/stretchr/testify/require"
)
type mockUserResolver struct {
- users map[string]string
- containers map[string]string
- namespace string
+ users map[string]string
+ buckets map[string]string
}
-func newMockUserResolver(accountUsers []string, buckets []string, namespace string) *mockUserResolver {
+func newMockUserResolver(accountUsers []string, buckets []string) *mockUserResolver {
userMap := make(map[string]string, len(accountUsers))
for _, user := range accountUsers {
userMap[user] = user + "/resolvedValue"
}
- containerMap := make(map[string]string, len(buckets))
+ bucketMap := make(map[string]string, len(buckets))
for _, bkt := range buckets {
- containerMap[bkt] = bkt + "/resolvedValues"
+ bucketMap[bkt] = bkt + "/resolvedValues"
}
- return &mockUserResolver{users: userMap, containers: containerMap, namespace: namespace}
+ return &mockUserResolver{users: userMap, buckets: bucketMap}
}
func (m *mockUserResolver) GetUserAddress(account, user string) (string, error) {
@@ -55,13 +53,13 @@ func (m *mockUserResolver) GetUserKey(account, user string) (string, error) {
return key, nil
}
-func (m *mockUserResolver) GetBucketInfo(bkt string) (*BucketInfo, error) {
- cnr, ok := m.containers[bkt]
+func (m *mockUserResolver) GetBucketCID(bkt string) (string, error) {
+ cnrID, ok := m.buckets[bkt]
if !ok {
- return nil, errors.New("not found")
+ return "", errors.New("not found")
}
- return &BucketInfo{Container: cnr, Namespace: m.namespace}, nil
+ return cnrID, nil
}
func TestConverters(t *testing.T) {
@@ -71,11 +69,10 @@ func TestConverters(t *testing.T) {
principal := "arn:aws:iam::" + namespace + ":user/" + userName
bktName := "DOC-EXAMPLE-BUCKET"
objName := "object-name"
- resource := fmt.Sprintf(s3.ResourceFormatS3BucketObjects, bktName)
- s3GetObjectAction := "s3:GetObject"
- s3HeadObjectAction := "s3:HeadObject"
+ resource := bktName + "/*"
+ action := "PutObject"
- mockResolver := newMockUserResolver([]string{user}, []string{bktName}, namespace)
+ mockResolver := newMockUserResolver([]string{user}, []string{bktName})
t.Run("valid policy", func(t *testing.T) {
p := Policy{
@@ -85,8 +82,8 @@ func TestConverters(t *testing.T) {
AWSPrincipalType: {principal},
},
Effect: AllowEffect,
- Action: []string{s3GetObjectAction},
- Resource: []string{resource},
+ Action: []string{"s3:PutObject"},
+ Resource: []string{"arn:aws:s3:::" + resource},
Conditions: map[string]Condition{
CondStringEquals: {
"s3:RequestObjectTag/Department": {"Finance"},
@@ -98,20 +95,20 @@ func TestConverters(t *testing.T) {
expected := &chain.Chain{Rules: []chain.Rule{
{
Status: chain.Allow,
- Actions: chain.Actions{Names: []string{s3GetObjectAction, s3HeadObjectAction}},
+ Actions: chain.Actions{Names: []string{action}},
Resources: chain.Resources{Names: []string{resource}},
Condition: []chain.Condition{
{
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: s3.PropertyKeyOwner,
- Value: mockResolver.users[user],
+ Op: chain.CondStringEquals,
+ Object: chain.ObjectRequest,
+ Key: s3.PropertyKeyOwner,
+ Value: mockResolver.users[user],
},
{
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: "s3:RequestObjectTag/Department",
- Value: "Finance",
+ Op: chain.CondStringEquals,
+ Object: chain.ObjectRequest,
+ Key: "s3:RequestObjectTag/Department",
+ Value: "Finance",
},
},
},
@@ -119,7 +116,7 @@ func TestConverters(t *testing.T) {
s3Chain, err := ConvertToS3Chain(p, mockResolver)
require.NoError(t, err)
- assertChainsEqual(t, expected, s3Chain)
+ require.Equal(t, expected, s3Chain)
})
t.Run("valid native policy", func(t *testing.T) {
@@ -131,25 +128,21 @@ func TestConverters(t *testing.T) {
},
Effect: AllowEffect,
Action: []string{"s3:PutObject"},
- Resource: []string{resource},
+ Resource: []string{"arn:aws:s3:::" + resource},
}},
}
expected := &chain.Chain{Rules: []chain.Rule{
{
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{native.MethodGetContainer, native.MethodPutObject,
- native.MethodGetObject, native.MethodHeadObject, native.MethodRangeObject}},
- Resources: chain.Resources{Names: []string{
- fmt.Sprintf(native.ResourceFormatNamespaceContainerObjects, namespace, mockResolver.containers[bktName]),
- fmt.Sprintf(native.ResourceFormatNamespaceContainer, namespace, mockResolver.containers[bktName])},
- },
+ Status: chain.Allow,
+ Actions: chain.Actions{Names: []string{action}},
+ Resources: chain.Resources{Names: []string{fmt.Sprintf(native.ResourceFormatRootContainerObjects, mockResolver.buckets[bktName])}},
Condition: []chain.Condition{
{
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: native.PropertyKeyActorPublicKey,
- Value: mockResolver.users[user],
+ Op: chain.CondStringEquals,
+ Object: chain.ObjectRequest,
+ Key: native.PropertyKeyActorPublicKey,
+ Value: mockResolver.users[user],
},
},
},
@@ -157,7 +150,7 @@ func TestConverters(t *testing.T) {
nativeChain, err := ConvertToNativeChain(p, mockResolver)
require.NoError(t, err)
- assertChainsEqual(t, expected, nativeChain)
+ require.Equal(t, expected, nativeChain)
})
t.Run("valid inverted policy", func(t *testing.T) {
@@ -168,22 +161,22 @@ func TestConverters(t *testing.T) {
AWSPrincipalType: {principal},
},
Effect: DenyEffect,
- NotAction: []string{s3GetObjectAction},
- NotResource: []string{resource},
+ NotAction: []string{"s3:PutObject"},
+ NotResource: []string{"arn:aws:s3:::" + resource},
}},
}
expected := &chain.Chain{Rules: []chain.Rule{
{
Status: chain.AccessDenied,
- Actions: chain.Actions{Inverted: true, Names: []string{s3GetObjectAction, s3HeadObjectAction}},
+ Actions: chain.Actions{Inverted: true, Names: []string{action}},
Resources: chain.Resources{Inverted: true, Names: []string{resource}},
Condition: []chain.Condition{
{
- Op: chain.CondStringNotEquals,
- Kind: chain.KindRequest,
- Key: s3.PropertyKeyOwner,
- Value: mockResolver.users[user],
+ Op: chain.CondStringNotEquals,
+ Object: chain.ObjectRequest,
+ Key: s3.PropertyKeyOwner,
+ Value: mockResolver.users[user],
},
},
},
@@ -191,76 +184,49 @@ func TestConverters(t *testing.T) {
s3Chain, err := ConvertToS3Chain(p, mockResolver)
require.NoError(t, err)
- assertChainsEqual(t, expected, s3Chain)
+ require.Equal(t, expected, s3Chain)
})
- t.Run("valid native policy map action", func(t *testing.T) {
+ t.Run("valid policy map get action", func(t *testing.T) {
p := Policy{
Version: "2012-10-17",
Statement: []Statement{{
Principal: map[PrincipalType][]string{
AWSPrincipalType: {principal},
},
- Effect: AllowEffect,
- Action: []string{"s3:DeleteObject", "s3:DeleteBucket"},
- Resource: []string{
- fmt.Sprintf(s3.ResourceFormatS3BucketObject, bktName, objName),
- fmt.Sprintf(s3.ResourceFormatS3Bucket, bktName),
- },
+ Effect: DenyEffect,
+ NotAction: []string{"s3:GetObject"},
+ NotResource: []string{"arn:aws:s3:::" + bktName + "/" + objName},
}},
}
expected := &chain.Chain{Rules: []chain.Rule{
{
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{
- native.MethodGetContainer, native.MethodDeleteContainer,
- native.MethodSearchObject, native.MethodHeadObject,
- native.MethodDeleteObject, native.MethodPutObject,
- native.MethodGetObject, native.MethodRangeObject,
- }},
- Resources: chain.Resources{Names: []string{
- fmt.Sprintf(native.ResourceFormatNamespaceContainerObjects, namespace, mockResolver.containers[bktName]),
- fmt.Sprintf(native.ResourceFormatNamespaceContainer, namespace, mockResolver.containers[bktName]),
+ Status: chain.AccessDenied,
+ Actions: chain.Actions{Inverted: true, Names: actionToOpMap["GetObject"]},
+ Resources: chain.Resources{Inverted: true, Names: []string{
+ fmt.Sprintf(native.ResourceFormatRootContainerObjects, mockResolver.buckets[bktName]),
}},
Condition: []chain.Condition{
{
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: native.PropertyKeyActorPublicKey,
- Value: mockResolver.users[user],
+ Op: chain.CondStringEquals,
+ Object: chain.ObjectRequest,
+ Key: native.PropertyKeyActorPublicKey,
+ Value: mockResolver.users[user],
},
{
- Op: chain.CondStringLike,
- Kind: chain.KindResource,
- Key: PropertyKeyFilePath,
- Value: objName,
+ Op: chain.CondStringLike,
+ Object: chain.ObjectResource,
+ Key: PropertyKeyFilePath,
+ Value: objName,
},
},
},
- {
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{
- native.MethodGetContainer, native.MethodDeleteContainer,
- native.MethodSearchObject, native.MethodHeadObject,
- native.MethodDeleteObject, native.MethodPutObject,
- native.MethodGetObject, native.MethodRangeObject,
- }},
- Resources: chain.Resources{Names: []string{
- fmt.Sprintf(native.ResourceFormatNamespaceContainer, namespace, mockResolver.containers[bktName]),
- }},
- Condition: []chain.Condition{{
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: native.PropertyKeyActorPublicKey,
- Value: mockResolver.users[user],
- }},
- },
}}
nativeChain, err := ConvertToNativeChain(p, mockResolver)
require.NoError(t, err)
- assertChainsEqual(t, expected, nativeChain)
+ require.Equal(t, expected, nativeChain)
})
t.Run("invalid policy (unsupported principal type)", func(t *testing.T) {
@@ -312,66 +278,6 @@ func TestConverters(t *testing.T) {
_, err := ConvertToNativeChain(p, mockResolver)
require.Error(t, err)
})
-
- t.Run("invalid policy (missing s3 actions)", func(t *testing.T) {
- p := Policy{
- Version: "2012-10-17",
- Statement: []Statement{{
- Principal: map[PrincipalType][]string{
- AWSPrincipalType: {principal},
- },
- Effect: AllowEffect,
- Resource: []string{"arn:aws:s3:::" + resource},
- }},
- }
-
- _, err := ConvertToS3Chain(p, mockResolver)
- require.Error(t, err)
- })
-
- t.Run("valid mixed iam/s3 actions", func(t *testing.T) {
- p := Policy{
- Version: "2012-10-17",
- Statement: []Statement{{
- Principal: map[PrincipalType][]string{AWSPrincipalType: {principal}},
- Effect: AllowEffect,
- Action: []string{"s3:DeleteObject", "iam:*"},
- Resource: []string{"*"},
- }},
- }
-
- s3Expected := &chain.Chain{Rules: []chain.Rule{{
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{"s3:DeleteObject", "s3:DeleteMultipleObjects", "iam:*"}},
- Resources: chain.Resources{Names: []string{"*"}},
- Condition: []chain.Condition{{
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: s3.PropertyKeyOwner,
- Value: mockResolver.users[user],
- }},
- }}}
-
- s3Chain, err := ConvertToS3Chain(p, mockResolver)
- require.NoError(t, err)
- assertChainsEqual(t, s3Expected, s3Chain)
-
- nativeExpected := &chain.Chain{Rules: []chain.Rule{{
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{native.MethodGetContainer, native.MethodDeleteObject, native.MethodPutObject, native.MethodHeadObject, native.MethodGetObject, native.MethodRangeObject}},
- Resources: chain.Resources{Names: []string{native.ResourceFormatAllObjects, native.ResourceFormatAllContainers}},
- Condition: []chain.Condition{{
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: native.PropertyKeyActorPublicKey,
- Value: mockResolver.users[user],
- }},
- }}}
-
- nativeChain, err := ConvertToNativeChain(p, mockResolver)
- require.NoError(t, err)
- assertChainsEqual(t, nativeExpected, nativeChain)
- })
}
func TestConvertToChainCondition(t *testing.T) {
@@ -391,16 +297,12 @@ func TestConvertToChainCondition(t *testing.T) {
CondDateGreaterThan: {"key11": {"2006-01-02T15:04:05-01:00"}},
CondDateGreaterThanEquals: {"key12": {"2006-01-02T15:04:05-03:00"}},
CondBool: {"key13": {"True"}},
+ CondIPAddress: {"key14": {"val14"}},
+ CondNotIPAddress: {"key15": {"val15"}},
CondArnEquals: {"key16": {"val16"}},
CondArnLike: {condKeyAWSPrincipalARN: {principal}},
CondArnNotEquals: {"key18": {"val18"}},
CondArnNotLike: {"key19": {"val19"}},
- CondNumericEquals: {"key20": {"-20"}},
- CondNumericNotEquals: {"key21": {"+21"}},
- CondNumericLessThan: {"key22": {"0"}},
- CondNumericLessThanEquals: {"key23": {"23.23"}},
- CondNumericGreaterThan: {"key24": {"-24.24"}},
- CondNumericGreaterThanEquals: {"key25": {"+25.25"}},
}
expectedCondition := []GroupedConditions{
@@ -408,193 +310,161 @@ func TestConvertToChainCondition(t *testing.T) {
Any: true,
Conditions: []chain.Condition{
{
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: "key1",
- Value: "val0",
+ Op: chain.CondStringEquals,
+ Object: chain.ObjectRequest,
+ Key: "key1",
+ Value: "val0",
},
{
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: "key1",
- Value: "val1",
+ Op: chain.CondStringEquals,
+ Object: chain.ObjectRequest,
+ Key: "key1",
+ Value: "val1",
},
},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringNotEquals,
- Kind: chain.KindRequest,
- Key: "key2",
- Value: "val2",
+ Op: chain.CondStringNotEquals,
+ Object: chain.ObjectRequest,
+ Key: "key2",
+ Value: "val2",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringEqualsIgnoreCase,
- Kind: chain.KindRequest,
- Key: "key3",
- Value: "val3",
+ Op: chain.CondStringEqualsIgnoreCase,
+ Object: chain.ObjectRequest,
+ Key: "key3",
+ Value: "val3",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringNotEqualsIgnoreCase,
- Kind: chain.KindRequest,
- Key: "key4",
- Value: "val4",
+ Op: chain.CondStringNotEqualsIgnoreCase,
+ Object: chain.ObjectRequest,
+ Key: "key4",
+ Value: "val4",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringLike,
- Kind: chain.KindRequest,
- Key: "key5",
- Value: "val5",
+ Op: chain.CondStringLike,
+ Object: chain.ObjectRequest,
+ Key: "key5",
+ Value: "val5",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringNotLike,
- Kind: chain.KindRequest,
- Key: "key6",
- Value: "val6",
+ Op: chain.CondStringNotLike,
+ Object: chain.ObjectRequest,
+ Key: "key6",
+ Value: "val6",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: "key7",
- Value: "1136189045",
+ Op: chain.CondStringEquals,
+ Object: chain.ObjectRequest,
+ Key: "key7",
+ Value: "1136189045",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringNotEquals,
- Kind: chain.KindRequest,
- Key: "key8",
- Value: "1136214245",
+ Op: chain.CondStringNotEquals,
+ Object: chain.ObjectRequest,
+ Key: "key8",
+ Value: "1136214245",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringLessThan,
- Kind: chain.KindRequest,
- Key: "key9",
- Value: "1136192645",
+ Op: chain.CondStringLessThan,
+ Object: chain.ObjectRequest,
+ Key: "key9",
+ Value: "1136192645",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringLessThanEquals,
- Kind: chain.KindRequest,
- Key: "key10",
- Value: "1136203445",
+ Op: chain.CondStringLessThanEquals,
+ Object: chain.ObjectRequest,
+ Key: "key10",
+ Value: "1136203445",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringGreaterThan,
- Kind: chain.KindRequest,
- Key: "key11",
- Value: "1136217845",
+ Op: chain.CondStringGreaterThan,
+ Object: chain.ObjectRequest,
+ Key: "key11",
+ Value: "1136217845",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringGreaterThanEquals,
- Kind: chain.KindRequest,
- Key: "key12",
- Value: "1136225045",
+ Op: chain.CondStringGreaterThanEquals,
+ Object: chain.ObjectRequest,
+ Key: "key12",
+ Value: "1136225045",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringEqualsIgnoreCase,
- Kind: chain.KindRequest,
- Key: "key13",
- Value: "True",
+ Op: chain.CondStringEqualsIgnoreCase,
+ Object: chain.ObjectRequest,
+ Key: "key13",
+ Value: "True",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: "key16",
- Value: "val16",
+ Op: chain.CondStringLike,
+ Object: chain.ObjectRequest,
+ Key: "key14",
+ Value: "val14",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringLike,
- Kind: chain.KindRequest,
- Key: condKeyAWSPrincipalARN,
- Value: principal,
+ Op: chain.CondStringNotLike,
+ Object: chain.ObjectRequest,
+ Key: "key15",
+ Value: "val15",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringNotEquals,
- Kind: chain.KindRequest,
- Key: "key18",
- Value: "val18",
+ Op: chain.CondStringEquals,
+ Object: chain.ObjectRequest,
+ Key: "key16",
+ Value: "val16",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondStringNotLike,
- Kind: chain.KindRequest,
- Key: "key19",
- Value: "val19",
+ Op: chain.CondStringLike,
+ Object: chain.ObjectRequest,
+ Key: condKeyAWSPrincipalARN,
+ Value: principal,
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondNumericEquals,
- Kind: chain.KindRequest,
- Key: "key20",
- Value: "-20",
+ Op: chain.CondStringNotEquals,
+ Object: chain.ObjectRequest,
+ Key: "key18",
+ Value: "val18",
}},
},
{
Conditions: []chain.Condition{{
- Op: chain.CondNumericNotEquals,
- Kind: chain.KindRequest,
- Key: "key21",
- Value: "+21",
- }},
- },
- {
- Conditions: []chain.Condition{{
- Op: chain.CondNumericLessThan,
- Kind: chain.KindRequest,
- Key: "key22",
- Value: "0",
- }},
- },
- {
- Conditions: []chain.Condition{{
- Op: chain.CondNumericLessThanEquals,
- Kind: chain.KindRequest,
- Key: "key23",
- Value: "23.23",
- }},
- },
- {
- Conditions: []chain.Condition{{
- Op: chain.CondNumericGreaterThan,
- Kind: chain.KindRequest,
- Key: "key24",
- Value: "-24.24",
- }},
- },
- {
- Conditions: []chain.Condition{{
- Op: chain.CondNumericGreaterThanEquals,
- Kind: chain.KindRequest,
- Key: "key25",
- Value: "+25.25",
+ Op: chain.CondStringNotLike,
+ Object: chain.ObjectRequest,
+ Key: "key19",
+ Value: "val19",
}},
},
}
@@ -602,159 +472,6 @@ func TestConvertToChainCondition(t *testing.T) {
actualCondition, err := convertToChainCondition(conditions)
require.NoError(t, err)
require.ElementsMatch(t, expectedCondition, actualCondition)
-
- invalidConditions := []Condition{
- {"key1": {"invalid"}},
- {"key2": {"1 2"}},
- {"key3": {"0x12f"}},
- {"key4": {"0b1010"}},
- {"key5": {"+Inf"}},
- {"key6": {"-Inf"}},
- {"key7": {"inf"}},
- {"key8": {"NaN"}},
- {"key9": {"nan"}},
- }
-
- for _, cond := range invalidConditions {
- _, err = convertToChainCondition(Conditions{CondNumericEquals: cond})
- require.Error(t, err)
- }
-}
-
-func TestIPConditions(t *testing.T) {
- t.Run("ip converters", func(t *testing.T) {
- for _, tc := range []struct {
- ip string
- error bool
- expected string
- }{
- {ip: "203.0.113.0/24", expected: "203.0.113.0/24"},
- {ip: "203.0.113.1", expected: "203.0.113.1/32"},
- {ip: "203.0.113.1/", error: true},
- {ip: "203.0.113.1/33", error: true},
- {ip: "192.168.0.1/24", expected: "192.168.0.1/24"},
- {ip: "10.10.0.1/24", expected: "10.10.0.1/24"},
- {ip: "172.16.0.1/24", expected: "172.16.0.1/24"},
- {ip: "2001:DB8:1234:5678::/64", expected: "2001:DB8:1234:5678::/64"},
- {ip: "2001:DB8:1234:5678::", expected: "2001:DB8:1234:5678::/32"},
- {ip: "2001:DB8:1234:5678::/", error: true},
- {ip: "2001:DB8:1234:5678::/129", error: true},
- {ip: "FC00::/64", expected: "FC00::/64"},
- } {
- t.Run("", func(t *testing.T) {
- actual, err := ipConvertFunction(tc.ip)
- if tc.error {
- require.Error(t, err)
- return
- }
- require.NoError(t, err)
- require.Equal(t, tc.expected, actual)
- })
- }
- })
-
- t.Run("chain converters", func(t *testing.T) {
- for _, tc := range []struct {
- name string
- policy string
- }{
- {
- name: "wildcard principal",
- policy: `{"Version":"2012-10-17",
-"Statement":{"Effect":"Allow","Principal": "*","Action":"s3:*","Resource":"*","Condition": {"IpAddress": {"aws:SourceIp": "203.0.113.0/24"}}}
-}`,
- },
- {
- name: "wildcard AWS principal",
- policy: `{"Version":"2012-10-17",
-"Statement":{"Effect":"Allow","Principal": {"AWS":"*"},"Action":"s3:*","Resource":"*","Condition": {"IpAddress": {"aws:SourceIp": "203.0.113.0/24"}}}
-}`,
- },
- } {
- var p Policy
- err := json.Unmarshal([]byte(tc.policy), &p)
- require.NoError(t, err)
-
- s3Expected := &chain.Chain{
- Rules: []chain.Rule{{
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{"s3:*"}},
- Resources: chain.Resources{Names: []string{Wildcard}},
- Condition: []chain.Condition{{
- Op: chain.CondIPAddress,
- Kind: chain.KindRequest,
- Key: common.PropertyKeyFrostFSSourceIP,
- Value: "203.0.113.0/24",
- }},
- }},
- }
-
- s3Chain, err := ConvertToS3Chain(p, newMockUserResolver(nil, nil, ""))
- require.NoError(t, err)
- require.Equal(t, s3Expected, s3Chain)
-
- nativeExpected := &chain.Chain{
- Rules: []chain.Rule{{
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{Wildcard}},
- Resources: chain.Resources{Names: []string{native.ResourceFormatAllObjects, native.ResourceFormatAllContainers}},
- Condition: []chain.Condition{{
- Op: chain.CondIPAddress,
- Kind: chain.KindRequest,
- Key: common.PropertyKeyFrostFSSourceIP,
- Value: "203.0.113.0/24",
- }},
- }},
- }
-
- nativeChain, err := ConvertToNativeChain(p, newMockUserResolver(nil, nil, ""))
- require.NoError(t, err)
- require.Equal(t, nativeExpected, nativeChain)
- }
- })
-
- t.Run("matching rules", func(t *testing.T) {
- for _, tc := range []struct {
- name string
- policy string
- }{
- {
- name: "wildcard principal",
- policy: `{"Version":"2012-10-17",
-"Statement":{"Effect":"Allow","Principal": "*","Action":"s3:*","Resource":"*","Condition": {"IpAddress": {"aws:SourceIp": "203.0.113.0/24"}}}
-}`,
- },
- {
- name: "wildcard AWS principal",
- policy: `{"Version":"2012-10-17",
-"Statement":{"Effect":"Allow","Principal": {"AWS":"*"},"Action":"s3:*","Resource":"*","Condition": {"IpAddress": {"aws:SourceIp": "203.0.113.0/24"}}}
-}`,
- },
- } {
- var p Policy
- err := json.Unmarshal([]byte(tc.policy), &p)
- require.NoError(t, err)
-
- s3Chain, err := ConvertToS3Chain(p, newMockUserResolver(nil, nil, ""))
- require.NoError(t, err)
-
- s := inmemory.NewInMemory()
- _, _, err = s.MorphRuleChainStorage().AddMorphRuleChain(chain.S3, engine.NamespaceTarget(""), s3Chain)
- require.NoError(t, err)
-
- req := testutil.NewRequest("s3:CreateBucket", testutil.NewResource(fmt.Sprintf(s3.ResourceFormatS3Bucket, "bkt"), nil),
- map[string]string{common.PropertyKeyFrostFSSourceIP: "203.0.113.128"})
- status, _, err := s.IsAllowed(chain.S3, engine.NewRequestTargetWithNamespace(""), req)
- require.NoError(t, err)
- require.Equal(t, chain.Allow.String(), status.String())
-
- req = testutil.NewRequest("s3:CreateBucket", testutil.NewResource(fmt.Sprintf(s3.ResourceFormatS3Bucket, "bkt"), nil),
- map[string]string{common.PropertyKeyFrostFSSourceIP: "203.0.114.0"})
- status, _, err = s.IsAllowed(chain.S3, engine.NewRequestTargetWithNamespace(""), req)
- require.NoError(t, err)
- require.Equal(t, chain.NoRuleFound.String(), status.String())
- }
- })
}
func TestParsePrincipalARN(t *testing.T) {
@@ -827,13 +544,10 @@ func TestComplexNativeConditions(t *testing.T) {
key1, key2 := "key1", "key2"
val0, val1, val2 := "val0", "val1", "val2"
- mockResolver := newMockUserResolver([]string{user1, user2}, []string{bktName1, bktName2, bktName3}, "")
- nativeResource1 := fmt.Sprintf(native.ResourceFormatRootContainerObjects, mockResolver.containers[bktName1])
- nativeResource1cnr := fmt.Sprintf(native.ResourceFormatRootContainer, mockResolver.containers[bktName1])
- nativeResource2 := fmt.Sprintf(native.ResourceFormatRootContainerObjects, mockResolver.containers[bktName2])
- nativeResource2cnr := fmt.Sprintf(native.ResourceFormatRootContainer, mockResolver.containers[bktName2])
- nativeResource3 := fmt.Sprintf(native.ResourceFormatRootContainerObjects, mockResolver.containers[bktName3])
- nativeResource3cnr := fmt.Sprintf(native.ResourceFormatRootContainer, mockResolver.containers[bktName3])
+ mockResolver := newMockUserResolver([]string{user1, user2}, []string{bktName1, bktName2, bktName3})
+ nativeResource1 := fmt.Sprintf(native.ResourceFormatRootContainerObjects, mockResolver.buckets[bktName1])
+ nativeResource2 := fmt.Sprintf(native.ResourceFormatRootContainerObjects, mockResolver.buckets[bktName2])
+ nativeResource3 := fmt.Sprintf(native.ResourceFormatRootContainerObjects, mockResolver.buckets[bktName3])
p := Policy{
Version: "2012-10-17",
@@ -841,7 +555,7 @@ func TestComplexNativeConditions(t *testing.T) {
Principal: map[PrincipalType][]string{
AWSPrincipalType: {principal1, principal2},
},
- Effect: AllowEffect,
+ Effect: DenyEffect,
Action: []string{"s3:" + action},
Resource: []string{"arn:aws:s3:::" + resource1, "arn:aws:s3:::" + resource2, "arn:aws:s3:::" + resource3},
Conditions: map[string]Condition{
@@ -851,17 +565,17 @@ func TestComplexNativeConditions(t *testing.T) {
}},
}
- expectedStatus := chain.Allow
- expectedActions := chain.Actions{Names: actionToNativeOpMap["s3:"+action]}
- expectedResource1 := chain.Resources{Names: []string{nativeResource1, nativeResource1cnr}}
- expectedResource23 := chain.Resources{Names: []string{nativeResource2, nativeResource2cnr, nativeResource3, nativeResource3cnr}}
+ expectedStatus := chain.AccessDenied
+ expectedActions := chain.Actions{Names: actionToOpMap[action]}
+ expectedResource1 := chain.Resources{Names: []string{nativeResource1}}
+ expectedResource23 := chain.Resources{Names: []string{nativeResource2, nativeResource3}}
- user1Condition := chain.Condition{Op: chain.CondStringEquals, Kind: chain.KindRequest, Key: native.PropertyKeyActorPublicKey, Value: mockResolver.users[user1]}
- user2Condition := chain.Condition{Op: chain.CondStringEquals, Kind: chain.KindRequest, Key: native.PropertyKeyActorPublicKey, Value: mockResolver.users[user2]}
- objectName1Condition := chain.Condition{Op: chain.CondStringLike, Kind: chain.KindResource, Key: PropertyKeyFilePath, Value: objName1}
- key1val0Condition := chain.Condition{Op: chain.CondStringEquals, Kind: chain.KindRequest, Key: key1, Value: val0}
- key1val1Condition := chain.Condition{Op: chain.CondStringEquals, Kind: chain.KindRequest, Key: key1, Value: val1}
- key2val2Condition := chain.Condition{Op: chain.CondStringLike, Kind: chain.KindRequest, Key: key2, Value: val2}
+ user1Condition := chain.Condition{Op: chain.CondStringEquals, Object: chain.ObjectRequest, Key: native.PropertyKeyActorPublicKey, Value: mockResolver.users[user1]}
+ user2Condition := chain.Condition{Op: chain.CondStringEquals, Object: chain.ObjectRequest, Key: native.PropertyKeyActorPublicKey, Value: mockResolver.users[user2]}
+ objectName1Condition := chain.Condition{Op: chain.CondStringLike, Object: chain.ObjectResource, Key: PropertyKeyFilePath, Value: objName1}
+ key1val0Condition := chain.Condition{Op: chain.CondStringEquals, Object: chain.ObjectRequest, Key: key1, Value: val0}
+ key1val1Condition := chain.Condition{Op: chain.CondStringEquals, Object: chain.ObjectRequest, Key: key1, Value: val1}
+ key2val2Condition := chain.Condition{Op: chain.CondStringLike, Object: chain.ObjectRequest, Key: key2, Value: val2}
expected := &chain.Chain{Rules: []chain.Rule{
{
@@ -969,7 +683,7 @@ func TestComplexNativeConditions(t *testing.T) {
{
name: "bucket resource1, all conditions matched",
action: action,
- resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.containers[bktName2], "some-oid"),
+ resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.buckets[bktName2], "some-oid"),
resourceMap: map[string]string{
PropertyKeyFilePath: "any-object-name",
},
@@ -978,12 +692,12 @@ func TestComplexNativeConditions(t *testing.T) {
key1: val0,
key2: val2,
},
- status: chain.Allow,
+ status: chain.AccessDenied,
},
{
name: "bucket resource3, all conditions matched",
action: action,
- resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.containers[bktName3], "some-oid"),
+ resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.buckets[bktName3], "some-oid"),
resourceMap: map[string]string{
PropertyKeyFilePath: "any-object-name",
},
@@ -992,12 +706,12 @@ func TestComplexNativeConditions(t *testing.T) {
key1: val0,
key2: val2,
},
- status: chain.Allow,
+ status: chain.AccessDenied,
},
{
name: "bucket resource, user condition mismatched",
action: action,
- resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.containers[bktName2], "some-oid"),
+ resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.buckets[bktName2], "some-oid"),
resourceMap: map[string]string{
PropertyKeyFilePath: "any-object-name",
},
@@ -1010,7 +724,7 @@ func TestComplexNativeConditions(t *testing.T) {
{
name: "bucket resource, key2 condition mismatched",
action: action,
- resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.containers[bktName3], "some-oid"),
+ resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.buckets[bktName3], "some-oid"),
resourceMap: map[string]string{
PropertyKeyFilePath: "any-object-name",
},
@@ -1024,7 +738,7 @@ func TestComplexNativeConditions(t *testing.T) {
{
name: "bucket resource, key1 condition mismatched",
action: action,
- resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.containers[bktName2], "some-oid"),
+ resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.buckets[bktName2], "some-oid"),
resourceMap: map[string]string{
PropertyKeyFilePath: "any-object-name",
},
@@ -1037,7 +751,7 @@ func TestComplexNativeConditions(t *testing.T) {
{
name: "bucket/object resource, all conditions matched",
action: action,
- resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.containers[bktName1], "some-oid"),
+ resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.buckets[bktName1], "some-oid"),
resourceMap: map[string]string{
PropertyKeyFilePath: objName1,
},
@@ -1046,12 +760,12 @@ func TestComplexNativeConditions(t *testing.T) {
key1: val0,
key2: val2,
},
- status: chain.Allow,
+ status: chain.AccessDenied,
},
{
name: "bucket/object resource, user condition mismatched",
action: action,
- resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.containers[bktName1], "some-oid"),
+ resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.buckets[bktName1], "some-oid"),
resourceMap: map[string]string{
PropertyKeyFilePath: objName1,
},
@@ -1065,7 +779,7 @@ func TestComplexNativeConditions(t *testing.T) {
{
name: "bucket/object resource, key1 condition mismatched",
action: action,
- resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.containers[bktName1], "some-oid"),
+ resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.buckets[bktName1], "some-oid"),
resourceMap: map[string]string{
PropertyKeyFilePath: objName1,
},
@@ -1078,7 +792,7 @@ func TestComplexNativeConditions(t *testing.T) {
{
name: "bucket/object resource, key2 condition mismatched",
action: action,
- resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.containers[bktName1], "some-oid"),
+ resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.buckets[bktName1], "some-oid"),
resourceMap: map[string]string{
PropertyKeyFilePath: objName1,
},
@@ -1092,7 +806,7 @@ func TestComplexNativeConditions(t *testing.T) {
{
name: "bucket/object resource, object filepath condition mismatched",
action: action,
- resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.containers[bktName1], "some-oid"),
+ resource: fmt.Sprintf(native.ResourceFormatRootContainerObject, mockResolver.buckets[bktName1], "some-oid"),
resourceMap: map[string]string{
PropertyKeyFilePath: "any-object-name",
},
@@ -1135,16 +849,15 @@ func TestComplexS3Conditions(t *testing.T) {
principal2 := "arn:aws:iam::" + namespace + ":user/" + userName2
bktName1, bktName2, bktName3 := "bktName", "bktName2", "bktName3"
objName1 := "objName1"
- resource1 := fmt.Sprintf(s3.ResourceFormatS3BucketObject, bktName1, objName1)
- resource2 := fmt.Sprintf(s3.ResourceFormatS3BucketObjects, bktName2)
- resource3 := fmt.Sprintf(s3.ResourceFormatS3BucketObjects, bktName3)
- action := "s3:DeleteObject"
- action2 := "s3:DeleteMultipleObjects"
+ resource1 := bktName1 + "/" + objName1
+ resource2 := bktName2 + "/*"
+ resource3 := bktName3 + "/*"
+ action := "PutObject"
key1, key2 := "key1", "key2"
val0, val1, val2 := "val0", "val1", "val2"
- mockResolver := newMockUserResolver([]string{user1, user2}, []string{bktName1, bktName2, bktName3}, "")
+ mockResolver := newMockUserResolver([]string{user1, user2}, []string{bktName1, bktName2, bktName3})
p := Policy{
Version: "2012-10-17",
@@ -1153,8 +866,8 @@ func TestComplexS3Conditions(t *testing.T) {
AWSPrincipalType: {principal1, principal2},
},
Effect: DenyEffect,
- Action: []string{action},
- Resource: []string{resource1, resource2, resource3},
+ Action: []string{"s3:" + action},
+ Resource: []string{"arn:aws:s3:::" + resource1, "arn:aws:s3:::" + resource2, "arn:aws:s3:::" + resource3},
Conditions: map[string]Condition{
CondStringEquals: {key1: {val0, val1}},
CondStringLike: {key2: {val2}},
@@ -1163,14 +876,14 @@ func TestComplexS3Conditions(t *testing.T) {
}
expectedStatus := chain.AccessDenied
- expectedActions := chain.Actions{Names: []string{action, action2}}
+ expectedActions := chain.Actions{Names: actionToOpMap[action]}
expectedResources := chain.Resources{Names: []string{resource1, resource2, resource3}}
- user1Condition := chain.Condition{Op: chain.CondStringEquals, Kind: chain.KindRequest, Key: s3.PropertyKeyOwner, Value: mockResolver.users[user1]}
- user2Condition := chain.Condition{Op: chain.CondStringEquals, Kind: chain.KindRequest, Key: s3.PropertyKeyOwner, Value: mockResolver.users[user2]}
- key1val0Condition := chain.Condition{Op: chain.CondStringEquals, Kind: chain.KindRequest, Key: key1, Value: val0}
- key1val1Condition := chain.Condition{Op: chain.CondStringEquals, Kind: chain.KindRequest, Key: key1, Value: val1}
- key2val2Condition := chain.Condition{Op: chain.CondStringLike, Kind: chain.KindRequest, Key: key2, Value: val2}
+ user1Condition := chain.Condition{Op: chain.CondStringEquals, Object: chain.ObjectRequest, Key: s3.PropertyKeyOwner, Value: mockResolver.users[user1]}
+ user2Condition := chain.Condition{Op: chain.CondStringEquals, Object: chain.ObjectRequest, Key: s3.PropertyKeyOwner, Value: mockResolver.users[user2]}
+ key1val0Condition := chain.Condition{Op: chain.CondStringEquals, Object: chain.ObjectRequest, Key: key1, Value: val0}
+ key1val1Condition := chain.Condition{Op: chain.CondStringEquals, Object: chain.ObjectRequest, Key: key1, Value: val1}
+ key2val2Condition := chain.Condition{Op: chain.CondStringLike, Object: chain.ObjectRequest, Key: key2, Value: val2}
expected := &chain.Chain{Rules: []chain.Rule{
{
@@ -1245,7 +958,7 @@ func TestComplexS3Conditions(t *testing.T) {
{
name: "bucket resource3, all conditions matched",
action: action,
- resource: fmt.Sprintf(s3.ResourceFormatS3BucketObject, bktName3, "some-obj"),
+ resource: bktName3 + "/some-obj",
requestMap: map[string]string{
s3.PropertyKeyOwner: mockResolver.users[user1],
key1: val0,
@@ -1256,7 +969,7 @@ func TestComplexS3Conditions(t *testing.T) {
{
name: "bucket resource, user condition mismatched",
action: action,
- resource: fmt.Sprintf(s3.ResourceFormatS3BucketObject, bktName2, "some-obj"),
+ resource: bktName2 + "/some-obj",
requestMap: map[string]string{
key1: val0,
key2: val2,
@@ -1266,7 +979,7 @@ func TestComplexS3Conditions(t *testing.T) {
{
name: "bucket resource, key2 condition mismatched",
action: action,
- resource: fmt.Sprintf(s3.ResourceFormatS3BucketObject, bktName3, "some-obj"),
+ resource: bktName3 + "/some-obj",
requestMap: map[string]string{
s3.PropertyKeyOwner: mockResolver.users[user1],
key1: val0,
@@ -1277,7 +990,7 @@ func TestComplexS3Conditions(t *testing.T) {
{
name: "bucket resource, key1 condition mismatched",
action: action,
- resource: fmt.Sprintf(s3.ResourceFormatS3BucketObject, bktName2, "some-obj"),
+ resource: bktName2 + "/some-obj",
requestMap: map[string]string{
s3.PropertyKeyOwner: mockResolver.users[user1],
key2: val2,
@@ -1298,7 +1011,7 @@ func TestComplexS3Conditions(t *testing.T) {
{
name: "bucket/object resource, resource mismatched",
action: action,
- resource: fmt.Sprintf(s3.ResourceFormatS3BucketObject, bktName1, "some-obj"),
+ resource: bktName1 + "/some-obj",
requestMap: map[string]string{
s3.PropertyKeyOwner: mockResolver.users[user1],
key1: val0,
@@ -1341,7 +1054,7 @@ func TestComplexS3Conditions(t *testing.T) {
{
name: "resource mismatched",
action: action,
- resource: fmt.Sprintf(s3.ResourceFormatS3BucketObject, "some-bkt", "some-obj"),
+ resource: "some-bkt/some-obj",
requestMap: map[string]string{
s3.PropertyKeyOwner: mockResolver.users[user1],
key1: val0,
@@ -1359,313 +1072,71 @@ func TestComplexS3Conditions(t *testing.T) {
}
}
-func TestS3BucketResource(t *testing.T) {
- namespace := "ns"
- bktName1, bktName2 := "bucket1", "bucket2"
- chainName := chain.Name("name")
+func TestWildcardConverters(t *testing.T) {
+ policy := `{"Version":"2012-10-17","Statement":{"Effect":"Allow", "Principal": "*", "Action":"*","Resource":"*"}}`
- mockResolver := newMockUserResolver([]string{}, []string{}, "")
-
- p := Policy{
- Version: "2012-10-17",
- Statement: []Statement{
- {
- Principal: map[PrincipalType][]string{Wildcard: nil},
- Effect: DenyEffect,
- Action: []string{"s3:ListBucket"},
- Resource: []string{fmt.Sprintf(s3.ResourceFormatS3Bucket, bktName1)},
- },
- {
- Principal: map[PrincipalType][]string{Wildcard: nil},
- Effect: AllowEffect,
- Action: []string{"*"},
- Resource: []string{s3.ResourceFormatS3All},
- },
- },
- }
-
- s3Chain, err := ConvertToS3Chain(p, mockResolver)
+ var p Policy
+ err := json.Unmarshal([]byte(policy), &p)
require.NoError(t, err)
- s := inmemory.NewInMemory()
- _, _, err = s.MorphRuleChainStorage().AddMorphRuleChain(chainName, engine.NamespaceTarget(namespace), s3Chain)
+ _, err = ConvertToS3Chain(p, newMockUserResolver(nil, nil))
require.NoError(t, err)
- // check we match just "bucket1" resource
- req := testutil.NewRequest("s3:HeadBucket", testutil.NewResource(fmt.Sprintf(s3.ResourceFormatS3Bucket, bktName1), nil), nil)
- status, _, err := s.IsAllowed(chainName, engine.NewRequestTargetWithNamespace(namespace), req)
+ _, err = ConvertToNativeChain(p, newMockUserResolver(nil, nil))
require.NoError(t, err)
- require.Equal(t, chain.AccessDenied.String(), status.String())
-
- // check we match just "bucket2" resource
- req = testutil.NewRequest("s3:HeadBucket", testutil.NewResource(fmt.Sprintf(s3.ResourceFormatS3Bucket, bktName2), nil), nil)
- status, _, err = s.IsAllowed(chainName, engine.NewRequestTargetWithNamespace(namespace), req)
- require.NoError(t, err)
- require.Equal(t, chain.Allow.String(), status.String())
-
- // check we also match "bucket2/object" resource
- req = testutil.NewRequest("s3:PutObject", testutil.NewResource(fmt.Sprintf(s3.ResourceFormatS3BucketObject, bktName2, "object"), nil), nil)
- status, _, err = s.IsAllowed(chainName, engine.NewRequestTargetWithNamespace(namespace), req)
- require.NoError(t, err)
- require.Equal(t, chain.Allow.String(), status.String())
}
-func TestWildcardConverters(t *testing.T) {
+func TestActionParsing(t *testing.T) {
for _, tc := range []struct {
- name string
- policy string
+ action string
+ expected string
+ err bool
}{
{
- name: "wildcard principal",
- policy: `{"Version":"2012-10-17","Statement":{"Effect":"Allow", "Principal": "*", "Action":"s3:*","Resource":"*"}}`,
+ action: "withoutPrefix",
+ expected: "",
+ err: true,
},
{
- name: "wildcard AWS principal",
- policy: `{"Version":"2012-10-17","Statement":{"Effect":"Allow", "Principal": {"AWS":"*"}, "Action":"s3:*","Resource":"*"}}`,
+ action: "s3:*Object",
+ expected: "",
+ err: true,
+ },
+ {
+ action: "*",
+ expected: "*",
+ },
+ {
+ action: "s3:PutObject",
+ expected: "PutObject",
+ },
+ {
+ action: "s3:Put*",
+ expected: "Put*",
+ },
+ {
+ action: "s3:*",
+ expected: "*",
+ },
+ {
+ action: "s3:",
+
+ expected: "",
},
} {
- t.Run(tc.name, func(t *testing.T) {
- var p Policy
- err := json.Unmarshal([]byte(tc.policy), &p)
- require.NoError(t, err)
-
- s3Expected := &chain.Chain{
- Rules: []chain.Rule{{
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{"s3:*"}},
- Resources: chain.Resources{Names: []string{Wildcard}},
- }},
+ t.Run("", func(t *testing.T) {
+ actual, err := parseActionAsS3Action(tc.action)
+ if tc.err {
+ require.Error(t, err)
+ return
}
- s3Chain, err := ConvertToS3Chain(p, newMockUserResolver(nil, nil, ""))
require.NoError(t, err)
- require.Equal(t, s3Expected, s3Chain)
-
- nativeExpected := &chain.Chain{
- Rules: []chain.Rule{{
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{Wildcard}},
- Resources: chain.Resources{Names: []string{native.ResourceFormatAllObjects, native.ResourceFormatAllContainers}},
- }},
- }
-
- nativeChain, err := ConvertToNativeChain(p, newMockUserResolver(nil, nil, ""))
- require.NoError(t, err)
- require.Equal(t, nativeExpected, nativeChain)
+ require.Equal(t, tc.expected, actual)
})
}
}
-func TestWildcardObjectsConverters(t *testing.T) {
- for _, tc := range []struct {
- name string
- policy string
- }{
- {
- name: "wildcard principal",
- policy: `{"Version":"2012-10-17","Statement":{"Effect":"Allow", "Principal": "*", "Action":"s3:*","Resource":"arn:aws:s3:::bucket/*"}}`,
- },
- {
- name: "wildcard AWS principal",
- policy: `{"Version":"2012-10-17","Statement":{"Effect":"Allow", "Principal": {"AWS":"*"}, "Action":"s3:*","Resource":"arn:aws:s3:::bucket/*"}}`,
- },
- } {
- var p Policy
- err := json.Unmarshal([]byte(tc.policy), &p)
- require.NoError(t, err)
-
- s3Expected := &chain.Chain{
- Rules: []chain.Rule{{
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{"s3:*"}},
- Resources: chain.Resources{Names: []string{"arn:aws:s3:::bucket/*"}},
- }},
- }
-
- s3Chain, err := ConvertToS3Chain(p, newMockUserResolver(nil, nil, ""))
- require.NoError(t, err)
- require.Equal(t, s3Expected, s3Chain)
-
- mockResolver := newMockUserResolver(nil, []string{"bucket"}, "")
-
- nativeExpected := &chain.Chain{
- Rules: []chain.Rule{{
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{Wildcard}},
- Resources: chain.Resources{Names: []string{
- fmt.Sprintf(native.ResourceFormatRootContainer, mockResolver.containers["bucket"]),
- fmt.Sprintf(native.ResourceFormatRootContainerObjects, mockResolver.containers["bucket"]),
- }},
- }},
- }
-
- nativeChain, err := ConvertToNativeChain(p, mockResolver)
- require.NoError(t, err)
- assertChainsEqual(t, nativeExpected, nativeChain)
- }
-}
-
-func TestDisableNativeDeny(t *testing.T) {
- for _, tc := range []struct {
- name string
- policy string
- }{
- {
- name: "wildcard principal",
- policy: `
-{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Deny",
- "Principal": "*",
- "Action": "s3:*",
- "Resource": [ "arn:aws:s3:::test-bucket/*" ]
- }
- ]
-}
-`,
- },
- {
- name: "wildcard AWS principal",
- policy: `
-{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Deny",
- "Principal": {"AWS":"*"},
- "Action": "s3:*",
- "Resource": [ "arn:aws:s3:::test-bucket/*" ]
- }
- ]
-}
-`,
- },
- } {
- var p Policy
- err := json.Unmarshal([]byte(tc.policy), &p)
- require.NoError(t, err)
-
- _, err = ConvertToNativeChain(p, newMockUserResolver(nil, nil, ""))
- require.ErrorIs(t, err, ErrActionsNotApplicable)
- }
-}
-
-func TestFromActions(t *testing.T) {
- t.Run("s3 actions", func(t *testing.T) {
- for _, tc := range []struct {
- action string
- res []string
- err bool
- }{
- {
- action: "withoutPrefix",
- err: true,
- },
- {
- action: "s3:*Object",
- err: true,
- },
- {
- action: "*",
- res: []string{Wildcard},
- },
- {
- action: "s3:PutObject",
- res: []string{"s3:PutObject", "s3:PostObject", "s3:CopyObject",
- "s3:UploadPart", "s3:UploadPartCopy", "s3:CreateMultipartUpload", "s3:CompleteMultipartUpload"},
- },
- {
- action: "s3:Put*",
- err: true,
- },
- {
- action: "s3:*",
- res: []string{"s3:*"},
- },
- {
- action: "s3:",
- err: true,
- },
- {
- action: "iam:ListAccessKeys",
- res: []string{"iam:ListAccessKeys"},
- },
- {
- action: "iam:*",
- res: []string{"iam:*"},
- },
- } {
- t.Run("", func(t *testing.T) {
- actions, err := formS3ActionNames([]string{tc.action})
- if tc.err {
- require.Error(t, err)
- } else {
- require.NoError(t, err)
- require.ElementsMatch(t, tc.res, actions)
- }
- })
- }
- })
-
- t.Run("native actions", func(t *testing.T) {
- for _, tc := range []struct {
- action string
- res []string
- err bool
- }{
- {
- action: "withoutPrefix",
- err: true,
- },
- {
- action: "s3:*Object",
- err: true,
- },
- {
- action: "*",
- res: []string{Wildcard},
- },
- {
- action: "s3:PutObject",
- res: []string{native.MethodGetContainer, native.MethodPutObject,
- native.MethodGetObject, native.MethodHeadObject, native.MethodRangeObject},
- },
- {
- action: "s3:Put*",
- err: true,
- },
- {
- action: "s3:*",
- res: []string{Wildcard},
- },
- {
- action: "s3:",
- err: true,
- },
- {
- action: "iam:ListAccessKeys",
- res: []string{},
- },
- {
- action: "iam:*",
- res: []string{},
- },
- } {
- t.Run("", func(t *testing.T) {
- actions, err := formNativeActionNames([]string{tc.action})
- if tc.err {
- require.Error(t, err)
- } else {
- require.NoError(t, err)
- require.ElementsMatch(t, tc.res, actions)
- }
- })
- }
- })
-}
-
func TestPrincipalParsing(t *testing.T) {
for _, tc := range []struct {
principal string
@@ -1731,244 +1202,59 @@ func TestPrincipalParsing(t *testing.T) {
func TestResourceParsing(t *testing.T) {
for _, tc := range []struct {
- resource string
- err bool
+ resource string
+ expectedBucket string
+ expectedObject string
+ err bool
}{
- {resource: "withoutPrefixAnd", err: true},
- {resource: "arn:aws:s3:::*/obj", err: true},
- {resource: "arn:aws:s3:::bkt/*"},
- {resource: "arn:aws:s3:::bkt"},
- {resource: "arn:aws:s3:::bkt/"},
- {resource: "arn:aws:s3:::*"},
- {resource: "*"},
+ {
+ resource: "withoutPrefixAnd",
+ err: true,
+ },
+ {
+ resource: "arn:aws:s3:::*/obj",
+ err: true,
+ },
+ {
+ resource: "arn:aws:s3:::bkt/*",
+ expectedBucket: "bkt",
+ expectedObject: "*",
+ },
+ {
+ resource: "arn:aws:s3:::bkt",
+ expectedBucket: "bkt",
+ expectedObject: "*",
+ },
+ {
+ resource: "arn:aws:s3:::bkt/",
+ expectedBucket: "bkt",
+ expectedObject: "*",
+ },
+ {
+ resource: "arn:aws:s3:::*",
+ expectedBucket: "*",
+ expectedObject: "*",
+ },
+ {
+ resource: "*",
+ expectedBucket: "*",
+ expectedObject: "*",
+ },
} {
t.Run("", func(t *testing.T) {
- err := validateResource(tc.resource)
+ bkt, obj, err := parseResourceAsS3ARN(tc.resource)
if tc.err {
require.Error(t, err)
- } else {
- require.NoError(t, err)
+ return
}
+
+ require.NoError(t, err)
+ require.Equal(t, tc.expectedBucket, bkt)
+ require.Equal(t, tc.expectedObject, obj)
})
}
}
-func TestTagsConditions(t *testing.T) {
- expectedS3Conditions := []chain.Condition{
- {
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: fmt.Sprintf(common.PropertyKeyFormatFrostFSIDUserClaim, "tag-department"),
- Value: "hr",
- },
- {
- Op: chain.CondStringEquals,
- Kind: chain.KindResource,
- Key: fmt.Sprintf(s3.PropertyKeyFormatResourceTag, "owner"),
- Value: "hr-admin",
- },
- {
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: fmt.Sprintf(s3.PropertyKeyFormatRequestTag, "scope"),
- Value: "*",
- },
- }
-
- expectedNativeConditions := []chain.Condition{
- {
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: fmt.Sprintf(common.PropertyKeyFormatFrostFSIDUserClaim, "tag-department"),
- Value: "hr",
- },
- }
-
- for _, tc := range []struct {
- name string
- policy string
- }{
- {
- name: "wildcard principal",
- policy: `
-{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Principal": "*",
- "Action": "s3:PutObjectTagging",
- "Resource": "*",
- "Condition": {
- "StringEquals": {
- "aws:PrincipalTag/department": "hr",
- "aws:ResourceTag/owner": "hr-admin",
- "aws:RequestTag/scope": "*"
- }
- }
- }
- ]
-}
-`,
- },
- {
- name: "wildcard AWS principal",
- policy: `
-{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Principal": {"AWS":"*"},
- "Action": "s3:PutObjectTagging",
- "Resource": "*",
- "Condition": {
- "StringEquals": {
- "aws:PrincipalTag/department": "hr",
- "aws:ResourceTag/owner": "hr-admin",
- "aws:RequestTag/scope": "*"
- }
- }
- }
- ]
-}
-`,
- },
- } {
- var p Policy
- err := json.Unmarshal([]byte(tc.policy), &p)
- require.NoError(t, err)
-
- s3Chain, err := ConvertToS3Chain(p, newMockUserResolver(nil, nil, ""))
- require.NoError(t, err)
- require.Len(t, s3Chain.Rules, 1)
- require.ElementsMatch(t, expectedS3Conditions, s3Chain.Rules[0].Condition)
-
- nativeChain, err := ConvertToNativeChain(p, newMockUserResolver(nil, nil, ""))
- require.NoError(t, err)
- require.Len(t, nativeChain.Rules, 1)
- require.ElementsMatch(t, expectedNativeConditions, nativeChain.Rules[0].Condition)
- }
-}
-
-func TestMFACondition(t *testing.T) {
- for _, tc := range []struct {
- name string
- policy string
- expectedValue string
- }{
- {
- name: "wildcard principal, mfa present true",
- policy: `
-{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Principal": "*",
- "Action": "s3:PutObject",
- "Resource": "*",
- "Condition": {
- "Bool": {
- "aws:MultiFactorAuthPresent": "true"
- }
- }
- }
- ]
-}
-`,
- expectedValue: "true",
- },
- {
- name: "wildcard AWS principal, mfa present true",
- policy: `
-{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Principal": {"AWS":"*"},
- "Action": "s3:PutObject",
- "Resource": "*",
- "Condition": {
- "Bool": {
- "aws:MultiFactorAuthPresent": "true"
- }
- }
- }
- ]
-}
-`,
- expectedValue: "true",
- },
- {
- name: "wildcard principal, mfa present false",
- policy: `
-{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Principal": "*",
- "Action": "s3:GetObject",
- "Resource": "*",
- "Condition": {
- "Bool": {
- "aws:MultiFactorAuthPresent": "false"
- }
- }
- }
- ]
-}
-`,
- expectedValue: "false",
- },
- {
- name: "wildcard AWS principal, mfa present false",
- policy: `
-{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Principal": {"AWS":"*"},
- "Action": "s3:GetObject",
- "Resource": "*",
- "Condition": {
- "Bool": {
- "aws:MultiFactorAuthPresent": "false"
- }
- }
- }
- ]
-}
-`,
- expectedValue: "false",
- },
- } {
- expectedConditions := []chain.Condition{
- {
- Op: chain.CondStringEqualsIgnoreCase,
- Kind: chain.KindRequest,
- Key: s3.PropertyKeyAccessBoxAttrMFA,
- Value: tc.expectedValue,
- },
- }
-
- var p Policy
- err := json.Unmarshal([]byte(tc.policy), &p)
- require.NoError(t, err)
-
- s3Chain, err := ConvertToS3Chain(p, newMockUserResolver(nil, nil, ""))
- require.NoError(t, err)
- require.Len(t, s3Chain.Rules, 1)
- require.ElementsMatch(t, expectedConditions, s3Chain.Rules[0].Condition)
-
- _, err = ConvertToNativeChain(p, newMockUserResolver(nil, nil, ""))
- require.ErrorIs(t, err, ErrActionsNotApplicable)
- }
-}
-
func requireChainRulesMatch(t *testing.T, expected, actual []chain.Rule) {
require.Equal(t, len(expected), len(actual), "length of chain rules differ")
@@ -1999,39 +1285,19 @@ func areRulesMatched(rule1, rule2 chain.Rule) bool {
return false
}
+ for i, name := range rule1.Resources.Names {
+ if name != rule2.Resources.Names[i] {
+ return false
+ }
+ }
+
+ for i, name := range rule1.Actions.Names {
+ if name != rule2.Actions.Names[i] {
+ return false
+ }
+ }
+
seen := make(map[int]struct{})
- for _, name1 := range rule1.Resources.Names {
- for j, name2 := range rule2.Resources.Names {
- if _, ok := seen[j]; ok {
- continue
- }
- if name1 == name2 {
- seen[j] = struct{}{}
- break
- }
- }
- }
- if len(seen) != len(rule1.Resources.Names) {
- return false
- }
-
- seen = make(map[int]struct{})
- for _, name1 := range rule1.Actions.Names {
- for j, name2 := range rule2.Actions.Names {
- if _, ok := seen[j]; ok {
- continue
- }
- if name1 == name2 {
- seen[j] = struct{}{}
- break
- }
- }
- }
- if len(seen) != len(rule1.Actions.Names) {
- return false
- }
-
- seen = make(map[int]struct{})
for _, cond1 := range rule1.Condition {
for j, cond2 := range rule2.Condition {
if _, ok := seen[j]; ok {
@@ -2046,19 +1312,3 @@ func areRulesMatched(rule1, rule2 chain.Rule) bool {
return len(seen) == len(rule1.Condition)
}
-
-func assertChainsEqual(t *testing.T, chain1, chain2 *chain.Chain) {
- require.Equal(t, string(chain1.ID), string(chain2.ID))
- require.Equal(t, chain1.MatchType, chain2.MatchType)
- require.Equal(t, len(chain1.Rules), len(chain2.Rules))
-
- for i, rule := range chain1.Rules {
- require.Equal(t, rule.Any, chain2.Rules[i].Any)
- require.Equal(t, rule.Resources.Inverted, chain2.Rules[i].Resources.Inverted)
- require.ElementsMatch(t, rule.Resources.Names, chain2.Rules[i].Resources.Names)
- require.Equal(t, rule.Status, chain2.Rules[i].Status)
- require.ElementsMatch(t, rule.Condition, chain2.Rules[i].Condition)
- require.Equal(t, rule.Actions.Inverted, chain2.Rules[i].Actions.Inverted)
- require.ElementsMatch(t, rule.Actions.Names, chain2.Rules[i].Actions.Names)
- }
-}
diff --git a/iam/policy.go b/iam/policy.go
index e38bb81..d5649fa 100644
--- a/iam/policy.go
+++ b/iam/policy.go
@@ -46,8 +46,6 @@ type (
PrincipalType string
)
-const policyVersion = "2012-10-17"
-
const (
GeneralPolicyType PolicyType = iota
IdentityBasedPolicyType
@@ -112,15 +110,6 @@ func (p *Principal) UnmarshalJSON(data []byte) error {
return err
}
- val, ok := m[AWSPrincipalType]
- if ok {
- str, ok = val.(string)
- if ok && str == Wildcard && len(m) == 1 {
- (*p)[Wildcard] = nil
- return nil
- }
- }
-
for key, val := range m {
element, ok := val.(string)
if ok {
@@ -233,20 +222,11 @@ func (p Policy) Validate(typ PolicyType) error {
}
func (p Policy) validate() error {
- if p.Version != policyVersion {
- return fmt.Errorf("invalid policy version, expected '%s', actual: '%s'", policyVersion, p.Version)
- }
-
if len(p.Statement) == 0 {
return errors.New("'Statement' is missing")
}
- sids := make(map[string]struct{}, len(p.Statement))
for _, statement := range p.Statement {
- if _, ok := sids[statement.SID]; ok && statement.SID != "" {
- return fmt.Errorf("duplicate 'SID': %s", statement.SID)
- }
- sids[statement.SID] = struct{}{}
if !statement.Effect.IsValid() {
return fmt.Errorf("unknown effect: '%s'", statement.Effect)
}
diff --git a/iam/policy_test.go b/iam/policy_test.go
index 79d93c6..c397f33 100644
--- a/iam/policy_test.go
+++ b/iam/policy_test.go
@@ -2,13 +2,14 @@ package iam
import (
"encoding/json"
+ "fmt"
"testing"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine/inmemory"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource/testutil"
- "git.frostfs.info/TrueCloudLab/policy-engine/schema/s3"
+ "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
"github.com/stretchr/testify/require"
)
@@ -217,7 +218,6 @@ func TestValidatePolicies(t *testing.T) {
{
name: "valid permission boundaries",
policy: Policy{
- Version: policyVersion,
Statement: []Statement{{
Effect: AllowEffect,
Action: []string{"s3:*", "cloudwatch:*", "ec2:*"},
@@ -230,7 +230,6 @@ func TestValidatePolicies(t *testing.T) {
{
name: "general invalid effect",
policy: Policy{
- Version: policyVersion,
Statement: []Statement{{
Effect: "dummy",
Action: []string{"s3:*", "cloudwatch:*", "ec2:*"},
@@ -243,7 +242,6 @@ func TestValidatePolicies(t *testing.T) {
{
name: "general invalid principal block",
policy: Policy{
- Version: policyVersion,
Statement: []Statement{{
Effect: AllowEffect,
Action: []string{"s3:*", "cloudwatch:*", "ec2:*"},
@@ -258,7 +256,6 @@ func TestValidatePolicies(t *testing.T) {
{
name: "general invalid not principal",
policy: Policy{
- Version: policyVersion,
Statement: []Statement{{
Effect: AllowEffect,
Action: []string{"s3:*", "cloudwatch:*", "ec2:*"},
@@ -272,7 +269,6 @@ func TestValidatePolicies(t *testing.T) {
{
name: "general invalid principal type",
policy: Policy{
- Version: policyVersion,
Statement: []Statement{{
Effect: AllowEffect,
Action: []string{"s3:*", "cloudwatch:*", "ec2:*"},
@@ -286,7 +282,6 @@ func TestValidatePolicies(t *testing.T) {
{
name: "general invalid action block",
policy: Policy{
- Version: policyVersion,
Statement: []Statement{{
Effect: AllowEffect,
Action: []string{"s3:*", "cloudwatch:*", "ec2:*"},
@@ -300,7 +295,6 @@ func TestValidatePolicies(t *testing.T) {
{
name: "general invalid resource block",
policy: Policy{
- Version: policyVersion,
Statement: []Statement{{
Effect: AllowEffect,
Resource: []string{Wildcard},
@@ -313,7 +307,6 @@ func TestValidatePolicies(t *testing.T) {
{
name: "invalid resource block",
policy: Policy{
- Version: policyVersion,
Statement: []Statement{{
Effect: AllowEffect,
Resource: []string{},
@@ -326,7 +319,6 @@ func TestValidatePolicies(t *testing.T) {
{
name: "missing resource block",
policy: Policy{
- Version: policyVersion,
Statement: []Statement{{
Effect: AllowEffect,
}},
@@ -340,43 +332,9 @@ func TestValidatePolicies(t *testing.T) {
typ: GeneralPolicyType,
isValid: false,
},
- {
- name: "duplicate sid",
- policy: Policy{
- Version: policyVersion,
- Statement: []Statement{
- {
- SID: "sid",
- Effect: AllowEffect,
- Action: []string{"s3:*"},
- Resource: []string{Wildcard},
- },
- {
- SID: "sid",
- Effect: AllowEffect,
- Action: []string{"cloudwatch:*"},
- Resource: []string{Wildcard},
- }},
- },
- typ: GeneralPolicyType,
- isValid: false,
- },
- {
- name: "missing version",
- policy: Policy{
- Statement: []Statement{{
- Effect: AllowEffect,
- Action: []string{"s3:*"},
- Resource: []string{Wildcard},
- }},
- },
- typ: GeneralPolicyType,
- isValid: false,
- },
{
name: "identity based valid",
policy: Policy{
- Version: policyVersion,
Statement: []Statement{{
Effect: AllowEffect,
Action: []string{"s3:PutObject"},
@@ -389,8 +347,7 @@ func TestValidatePolicies(t *testing.T) {
{
name: "identity based invalid because of id presence",
policy: Policy{
- ID: "some-id",
- Version: policyVersion,
+ ID: "some-id",
Statement: []Statement{{
Effect: AllowEffect,
Action: []string{"s3:PutObject"},
@@ -403,7 +360,6 @@ func TestValidatePolicies(t *testing.T) {
{
name: "identity based invalid because of principal presence",
policy: Policy{
- Version: policyVersion,
Statement: []Statement{{
Effect: AllowEffect,
Action: []string{"s3:PutObject"},
@@ -417,7 +373,6 @@ func TestValidatePolicies(t *testing.T) {
{
name: "identity based invalid because of not principal presence",
policy: Policy{
- Version: policyVersion,
Statement: []Statement{{
Effect: AllowEffect,
Action: []string{"s3:PutObject"},
@@ -431,7 +386,6 @@ func TestValidatePolicies(t *testing.T) {
{
name: "resource based valid principal",
policy: Policy{
- Version: policyVersion,
Statement: []Statement{{
Effect: DenyEffect,
Action: []string{"s3:PutObject"},
@@ -445,8 +399,7 @@ func TestValidatePolicies(t *testing.T) {
{
name: "resource based valid not principal",
policy: Policy{
- ID: "some-id",
- Version: policyVersion,
+ ID: "some-id",
Statement: []Statement{{
Effect: DenyEffect,
Action: []string{"s3:PutObject"},
@@ -460,8 +413,7 @@ func TestValidatePolicies(t *testing.T) {
{
name: "resource based invalid missing principal",
policy: Policy{
- ID: "some-id",
- Version: policyVersion,
+ ID: "some-id",
Statement: []Statement{{
Effect: AllowEffect,
Action: []string{"s3:PutObject"},
@@ -522,29 +474,29 @@ func TestProcessDenyFirst(t *testing.T) {
err = json.Unmarshal([]byte(resourceBasedPolicyStr), &resourcePolicy)
require.NoError(t, err)
- mockResolver := newMockUserResolver([]string{"root/user-name"}, []string{"test-bucket"}, "")
+ mockResolver := newMockUserResolver([]string{"root/user-name"}, []string{"test-bucket"})
- identityNativePolicy, err := ConvertToS3Chain(identityPolicy, mockResolver)
+ identityNativePolicy, err := ConvertToNativeChain(identityPolicy, mockResolver)
require.NoError(t, err)
identityNativePolicy.MatchType = chain.MatchTypeFirstMatch
- resourceNativePolicy, err := ConvertToS3Chain(resourcePolicy, mockResolver)
+ resourceNativePolicy, err := ConvertToNativeChain(resourcePolicy, mockResolver)
require.NoError(t, err)
s := inmemory.NewInMemory()
target := engine.NamespaceTarget("ns")
- _, _, err = s.MorphRuleChainStorage().AddMorphRuleChain(chain.S3, target, identityNativePolicy)
+ _, _, err = s.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, target, identityNativePolicy)
require.NoError(t, err)
- _, _, err = s.MorphRuleChainStorage().AddMorphRuleChain(chain.S3, target, resourceNativePolicy)
+ _, _, err = s.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, target, resourceNativePolicy)
require.NoError(t, err)
- resource := testutil.NewResource("arn:aws:s3:::test-bucket/object", nil)
- request := testutil.NewRequest("s3:PutObject", resource, map[string]string{s3.PropertyKeyOwner: mockResolver.users["root/user-name"]})
+ resource := testutil.NewResource(fmt.Sprintf(native.ResourceFormatRootContainerObjects, mockResolver.buckets["test-bucket"]), nil)
+ request := testutil.NewRequest("PutObject", resource, map[string]string{native.PropertyKeyActorPublicKey: mockResolver.users["root/user-name"]})
- status, found, err := s.IsAllowed(chain.S3, engine.NewRequestTarget("ns", ""), request)
+ status, found, err := s.IsAllowed(chain.Ingress, engine.NewRequestTarget("ns", ""), request)
require.NoError(t, err)
require.True(t, found)
require.Equal(t, chain.AccessDenied, status)
diff --git a/pkg/chain/chain.go b/pkg/chain/chain.go
index 5e94940..d241ca8 100644
--- a/pkg/chain/chain.go
+++ b/pkg/chain/chain.go
@@ -1,18 +1,16 @@
package chain
import (
+ "encoding/json"
"fmt"
- "net/netip"
"strings"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource"
"git.frostfs.info/TrueCloudLab/policy-engine/util"
- "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
- "golang.org/x/exp/slices"
)
// ID is the ID of rule chain.
-type ID []byte
+type ID string
// MatchType is the match type for chain rules.
type MatchType uint8
@@ -24,7 +22,6 @@ const (
MatchTypeFirstMatch MatchType = 1
)
-//easyjson:json
type Chain struct {
ID ID
@@ -34,7 +31,7 @@ type Chain struct {
}
func (c *Chain) Bytes() []byte {
- data, err := c.MarshalBinary()
+ data, err := json.Marshal(c)
if err != nil {
panic(err)
}
@@ -42,7 +39,7 @@ func (c *Chain) Bytes() []byte {
}
func (c *Chain) DecodeBytes(b []byte) error {
- return c.UnmarshalBinary(b)
+ return json.Unmarshal(b, c)
}
type Rule struct {
@@ -68,17 +65,17 @@ type Resources struct {
}
type Condition struct {
- Op ConditionType
- Kind ConditionKindType
- Key string
- Value string
+ Op ConditionType
+ Object ObjectType
+ Key string
+ Value string
}
-type ConditionKindType byte
+type ObjectType byte
const (
- KindResource ConditionKindType = iota
- KindRequest
+ ObjectResource ObjectType = iota
+ ObjectRequest
)
type ConditionType byte
@@ -106,64 +103,56 @@ const (
CondNumericLessThanEquals
CondNumericGreaterThan
CondNumericGreaterThanEquals
-
- CondSliceContains
-
- CondIPAddress
- CondNotIPAddress
)
-var condToStr = []struct {
- ct ConditionType
- str string
-}{
- {CondStringEquals, "StringEquals"},
- {CondStringNotEquals, "StringNotEquals"},
- {CondStringEqualsIgnoreCase, "StringEqualsIgnoreCase"},
- {CondStringNotEqualsIgnoreCase, "StringNotEqualsIgnoreCase"},
- {CondStringLike, "StringLike"},
- {CondStringNotLike, "StringNotLike"},
- {CondStringLessThan, "StringLessThan"},
- {CondStringLessThanEquals, "StringLessThanEquals"},
- {CondStringGreaterThan, "StringGreaterThan"},
- {CondStringGreaterThanEquals, "StringGreaterThanEquals"},
- {CondNumericEquals, "NumericEquals"},
- {CondNumericNotEquals, "NumericNotEquals"},
- {CondNumericLessThan, "NumericLessThan"},
- {CondNumericLessThanEquals, "NumericLessThanEquals"},
- {CondNumericGreaterThan, "NumericGreaterThan"},
- {CondNumericGreaterThanEquals, "NumericGreaterThanEquals"},
- {CondSliceContains, "SliceContains"},
- {CondIPAddress, "IPAddress"},
- {CondNotIPAddress, "NotIPAddress"},
-}
-
func (c ConditionType) String() string {
- for _, v := range condToStr {
- if v.ct == c {
- return v.str
- }
+ switch c {
+ case CondStringEquals:
+ return "StringEquals"
+ case CondStringNotEquals:
+ return "StringNotEquals"
+ case CondStringEqualsIgnoreCase:
+ return "StringEqualsIgnoreCase"
+ case CondStringNotEqualsIgnoreCase:
+ return "StringNotEqualsIgnoreCase"
+ case CondStringLike:
+ return "StringLike"
+ case CondStringNotLike:
+ return "StringNotLike"
+ case CondStringLessThan:
+ return "StringLessThan"
+ case CondStringLessThanEquals:
+ return "StringLessThanEquals"
+ case CondStringGreaterThan:
+ return "StringGreaterThan"
+ case CondStringGreaterThanEquals:
+ return "StringGreaterThanEquals"
+ case CondNumericEquals:
+ return "NumericEquals"
+ case CondNumericNotEquals:
+ return "NumericNotEquals"
+ case CondNumericLessThan:
+ return "NumericLessThan"
+ case CondNumericLessThanEquals:
+ return "NumericLessThanEquals"
+ case CondNumericGreaterThan:
+ return "NumericGreaterThan"
+ case CondNumericGreaterThanEquals:
+ return "NumericGreaterThanEquals"
+ default:
+ return "unknown condition type"
}
- return "unknown condition type"
-}
-
-const condSliceContainsDelimiter = "\x00"
-
-// FormCondSliceContainsValue builds value for ObjectResource or ObjectRequest property
-// that can be matched by CondSliceContains condition.
-func FormCondSliceContainsValue(values []string) string {
- return strings.Join(values, condSliceContainsDelimiter)
}
func (c *Condition) Match(req resource.Request) bool {
var val string
- switch c.Kind {
- case KindResource:
+ switch c.Object {
+ case ObjectResource:
val = req.Resource().Property(c.Key)
- case KindRequest:
+ case ObjectRequest:
val = req.Property(c.Key)
default:
- panic(fmt.Sprintf("unknown condition type: %d", c.Kind))
+ panic(fmt.Sprintf("unknown condition type: %d", c.Object))
}
switch c.Op {
@@ -189,63 +178,6 @@ func (c *Condition) Match(req resource.Request) bool {
return val > c.Value
case CondStringGreaterThanEquals:
return val >= c.Value
- case CondSliceContains:
- return slices.Contains(strings.Split(val, condSliceContainsDelimiter), c.Value)
- case CondNumericEquals, CondNumericNotEquals, CondNumericLessThan, CondNumericLessThanEquals, CondNumericGreaterThan,
- CondNumericGreaterThanEquals:
- return c.matchNumeric(val)
- case CondIPAddress, CondNotIPAddress:
- return c.matchIP(val)
- }
-}
-
-func (c *Condition) matchNumeric(val string) bool {
- valDecimal, err := fixedn.Fixed8FromString(val)
- if err != nil {
- return c.Op == CondNumericNotEquals
- }
-
- condVal, err := fixedn.Fixed8FromString(c.Value)
- if err != nil {
- return c.Op == CondNumericNotEquals
- }
-
- switch c.Op {
- default:
- panic(fmt.Sprintf("unimplemented: %d", c.Op))
- case CondNumericEquals:
- return valDecimal.Equal(condVal)
- case CondNumericNotEquals:
- return !valDecimal.Equal(condVal)
- case CondNumericLessThan:
- return valDecimal.LessThan(condVal)
- case CondNumericLessThanEquals:
- return valDecimal.LessThan(condVal) || valDecimal.Equal(condVal)
- case CondNumericGreaterThan:
- return valDecimal.GreaterThan(condVal)
- case CondNumericGreaterThanEquals:
- return valDecimal.GreaterThan(condVal) || valDecimal.Equal(condVal)
- }
-}
-
-func (c *Condition) matchIP(val string) bool {
- ipAddr, err := netip.ParseAddr(val)
- if err != nil {
- return false
- }
-
- prefix, err := netip.ParsePrefix(c.Value)
- if err != nil {
- return false
- }
-
- switch c.Op {
- default:
- panic(fmt.Sprintf("unimplemented: %d", c.Op))
- case CondIPAddress:
- return prefix.Contains(ipAddr)
- case CondNotIPAddress:
- return !prefix.Contains(ipAddr)
}
}
diff --git a/pkg/chain/chain_easyjson.go b/pkg/chain/chain_easyjson.go
deleted file mode 100644
index bf94c02..0000000
--- a/pkg/chain/chain_easyjson.go
+++ /dev/null
@@ -1,463 +0,0 @@
-// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
-
-package chain
-
-import (
- json "encoding/json"
- easyjson "github.com/mailru/easyjson"
- jlexer "github.com/mailru/easyjson/jlexer"
- jwriter "github.com/mailru/easyjson/jwriter"
-)
-
-// suppress unused package warning
-var (
- _ *json.RawMessage
- _ *jlexer.Lexer
- _ *jwriter.Writer
- _ easyjson.Marshaler
-)
-
-func easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain(in *jlexer.Lexer, out *Chain) {
- isTopLevel := in.IsStart()
- if in.IsNull() {
- if isTopLevel {
- in.Consumed()
- }
- in.Skip()
- return
- }
- in.Delim('{')
- for !in.IsDelim('}') {
- key := in.UnsafeFieldName(false)
- in.WantColon()
- if in.IsNull() {
- in.Skip()
- in.WantComma()
- continue
- }
- switch key {
- case "ID":
- if in.IsNull() {
- in.Skip()
- out.ID = nil
- } else {
- out.ID = in.Bytes()
- }
- case "Rules":
- if in.IsNull() {
- in.Skip()
- out.Rules = nil
- } else {
- in.Delim('[')
- if out.Rules == nil {
- if !in.IsDelim(']') {
- out.Rules = make([]Rule, 0, 0)
- } else {
- out.Rules = []Rule{}
- }
- } else {
- out.Rules = (out.Rules)[:0]
- }
- for !in.IsDelim(']') {
- var v2 Rule
- easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain1(in, &v2)
- out.Rules = append(out.Rules, v2)
- in.WantComma()
- }
- in.Delim(']')
- }
- case "MatchType":
- (out.MatchType).UnmarshalEasyJSON(in)
- default:
- in.SkipRecursive()
- }
- in.WantComma()
- }
- in.Delim('}')
- if isTopLevel {
- in.Consumed()
- }
-}
-func easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain(out *jwriter.Writer, in Chain) {
- out.RawByte('{')
- first := true
- _ = first
- {
- const prefix string = ",\"ID\":"
- out.RawString(prefix[1:])
- out.Base64Bytes(in.ID)
- }
- {
- const prefix string = ",\"Rules\":"
- out.RawString(prefix)
- if in.Rules == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
- out.RawString("null")
- } else {
- out.RawByte('[')
- for v5, v6 := range in.Rules {
- if v5 > 0 {
- out.RawByte(',')
- }
- easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain1(out, v6)
- }
- out.RawByte(']')
- }
- }
- {
- const prefix string = ",\"MatchType\":"
- out.RawString(prefix)
- (in.MatchType).MarshalEasyJSON(out)
- }
- out.RawByte('}')
-}
-
-// MarshalJSON supports json.Marshaler interface
-func (v Chain) MarshalJSON() ([]byte, error) {
- w := jwriter.Writer{}
- easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain(&w, v)
- return w.Buffer.BuildBytes(), w.Error
-}
-
-// MarshalEasyJSON supports easyjson.Marshaler interface
-func (v Chain) MarshalEasyJSON(w *jwriter.Writer) {
- easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain(w, v)
-}
-
-// UnmarshalJSON supports json.Unmarshaler interface
-func (v *Chain) UnmarshalJSON(data []byte) error {
- r := jlexer.Lexer{Data: data}
- easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain(&r, v)
- return r.Error()
-}
-
-// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
-func (v *Chain) UnmarshalEasyJSON(l *jlexer.Lexer) {
- easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain(l, v)
-}
-func easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain1(in *jlexer.Lexer, out *Rule) {
- isTopLevel := in.IsStart()
- if in.IsNull() {
- if isTopLevel {
- in.Consumed()
- }
- in.Skip()
- return
- }
- in.Delim('{')
- for !in.IsDelim('}') {
- key := in.UnsafeFieldName(false)
- in.WantColon()
- if in.IsNull() {
- in.Skip()
- in.WantComma()
- continue
- }
- switch key {
- case "Status":
- (out.Status).UnmarshalEasyJSON(in)
- case "Actions":
- easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain2(in, &out.Actions)
- case "Resources":
- easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain3(in, &out.Resources)
- case "Any":
- out.Any = bool(in.Bool())
- case "Condition":
- if in.IsNull() {
- in.Skip()
- out.Condition = nil
- } else {
- in.Delim('[')
- if out.Condition == nil {
- if !in.IsDelim(']') {
- out.Condition = make([]Condition, 0, 1)
- } else {
- out.Condition = []Condition{}
- }
- } else {
- out.Condition = (out.Condition)[:0]
- }
- for !in.IsDelim(']') {
- var v7 Condition
- easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain4(in, &v7)
- out.Condition = append(out.Condition, v7)
- in.WantComma()
- }
- in.Delim(']')
- }
- default:
- in.SkipRecursive()
- }
- in.WantComma()
- }
- in.Delim('}')
- if isTopLevel {
- in.Consumed()
- }
-}
-func easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain1(out *jwriter.Writer, in Rule) {
- out.RawByte('{')
- first := true
- _ = first
- {
- const prefix string = ",\"Status\":"
- out.RawString(prefix[1:])
- (in.Status).MarshalEasyJSON(out)
- }
- {
- const prefix string = ",\"Actions\":"
- out.RawString(prefix)
- easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain2(out, in.Actions)
- }
- {
- const prefix string = ",\"Resources\":"
- out.RawString(prefix)
- easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain3(out, in.Resources)
- }
- {
- const prefix string = ",\"Any\":"
- out.RawString(prefix)
- out.Bool(bool(in.Any))
- }
- {
- const prefix string = ",\"Condition\":"
- out.RawString(prefix)
- if in.Condition == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
- out.RawString("null")
- } else {
- out.RawByte('[')
- for v8, v9 := range in.Condition {
- if v8 > 0 {
- out.RawByte(',')
- }
- easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain4(out, v9)
- }
- out.RawByte(']')
- }
- }
- out.RawByte('}')
-}
-func easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain4(in *jlexer.Lexer, out *Condition) {
- isTopLevel := in.IsStart()
- if in.IsNull() {
- if isTopLevel {
- in.Consumed()
- }
- in.Skip()
- return
- }
- in.Delim('{')
- for !in.IsDelim('}') {
- key := in.UnsafeFieldName(false)
- in.WantColon()
- if in.IsNull() {
- in.Skip()
- in.WantComma()
- continue
- }
- switch key {
- case "Op":
- (out.Op).UnmarshalEasyJSON(in)
- case "Kind":
- (out.Kind).UnmarshalEasyJSON(in)
- case "Key":
- out.Key = string(in.String())
- case "Value":
- out.Value = string(in.String())
- default:
- in.SkipRecursive()
- }
- in.WantComma()
- }
- in.Delim('}')
- if isTopLevel {
- in.Consumed()
- }
-}
-func easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain4(out *jwriter.Writer, in Condition) {
- out.RawByte('{')
- first := true
- _ = first
- {
- const prefix string = ",\"Op\":"
- out.RawString(prefix[1:])
- (in.Op).MarshalEasyJSON(out)
- }
- {
- const prefix string = ",\"Kind\":"
- out.RawString(prefix)
- (in.Kind).MarshalEasyJSON(out)
- }
- {
- const prefix string = ",\"Key\":"
- out.RawString(prefix)
- out.String(string(in.Key))
- }
- {
- const prefix string = ",\"Value\":"
- out.RawString(prefix)
- out.String(string(in.Value))
- }
- out.RawByte('}')
-}
-func easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain3(in *jlexer.Lexer, out *Resources) {
- isTopLevel := in.IsStart()
- if in.IsNull() {
- if isTopLevel {
- in.Consumed()
- }
- in.Skip()
- return
- }
- in.Delim('{')
- for !in.IsDelim('}') {
- key := in.UnsafeFieldName(false)
- in.WantColon()
- if in.IsNull() {
- in.Skip()
- in.WantComma()
- continue
- }
- switch key {
- case "Inverted":
- out.Inverted = bool(in.Bool())
- case "Names":
- if in.IsNull() {
- in.Skip()
- out.Names = nil
- } else {
- in.Delim('[')
- if out.Names == nil {
- if !in.IsDelim(']') {
- out.Names = make([]string, 0, 4)
- } else {
- out.Names = []string{}
- }
- } else {
- out.Names = (out.Names)[:0]
- }
- for !in.IsDelim(']') {
- var v10 string
- v10 = string(in.String())
- out.Names = append(out.Names, v10)
- in.WantComma()
- }
- in.Delim(']')
- }
- default:
- in.SkipRecursive()
- }
- in.WantComma()
- }
- in.Delim('}')
- if isTopLevel {
- in.Consumed()
- }
-}
-func easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain3(out *jwriter.Writer, in Resources) {
- out.RawByte('{')
- first := true
- _ = first
- {
- const prefix string = ",\"Inverted\":"
- out.RawString(prefix[1:])
- out.Bool(bool(in.Inverted))
- }
- {
- const prefix string = ",\"Names\":"
- out.RawString(prefix)
- if in.Names == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
- out.RawString("null")
- } else {
- out.RawByte('[')
- for v11, v12 := range in.Names {
- if v11 > 0 {
- out.RawByte(',')
- }
- out.String(string(v12))
- }
- out.RawByte(']')
- }
- }
- out.RawByte('}')
-}
-func easyjsonE2758465DecodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain2(in *jlexer.Lexer, out *Actions) {
- isTopLevel := in.IsStart()
- if in.IsNull() {
- if isTopLevel {
- in.Consumed()
- }
- in.Skip()
- return
- }
- in.Delim('{')
- for !in.IsDelim('}') {
- key := in.UnsafeFieldName(false)
- in.WantColon()
- if in.IsNull() {
- in.Skip()
- in.WantComma()
- continue
- }
- switch key {
- case "Inverted":
- out.Inverted = bool(in.Bool())
- case "Names":
- if in.IsNull() {
- in.Skip()
- out.Names = nil
- } else {
- in.Delim('[')
- if out.Names == nil {
- if !in.IsDelim(']') {
- out.Names = make([]string, 0, 4)
- } else {
- out.Names = []string{}
- }
- } else {
- out.Names = (out.Names)[:0]
- }
- for !in.IsDelim(']') {
- var v13 string
- v13 = string(in.String())
- out.Names = append(out.Names, v13)
- in.WantComma()
- }
- in.Delim(']')
- }
- default:
- in.SkipRecursive()
- }
- in.WantComma()
- }
- in.Delim('}')
- if isTopLevel {
- in.Consumed()
- }
-}
-func easyjsonE2758465EncodeGitFrostfsInfoTrueCloudLabPolicyEnginePkgChain2(out *jwriter.Writer, in Actions) {
- out.RawByte('{')
- first := true
- _ = first
- {
- const prefix string = ",\"Inverted\":"
- out.RawString(prefix[1:])
- out.Bool(bool(in.Inverted))
- }
- {
- const prefix string = ",\"Names\":"
- out.RawString(prefix)
- if in.Names == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
- out.RawString("null")
- } else {
- out.RawByte('[')
- for v14, v15 := range in.Names {
- if v14 > 0 {
- out.RawByte(',')
- }
- out.String(string(v15))
- }
- out.RawByte(']')
- }
- }
- out.RawByte('}')
-}
diff --git a/pkg/chain/chain_names.go b/pkg/chain/chain_names.go
index 3e9ea5a..8bb88c0 100644
--- a/pkg/chain/chain_names.go
+++ b/pkg/chain/chain_names.go
@@ -7,7 +7,4 @@ const (
// Ingress represents chains applied when crossing user/storage network boundary.
// It is not applied when talking between nodes.
Ingress Name = "ingress"
-
- // S3 represents chains applied when crossing user/s3 network boundary.
- S3 Name = "s3"
)
diff --git a/pkg/chain/chain_test.go b/pkg/chain/chain_test.go
index a5154eb..911daa4 100644
--- a/pkg/chain/chain_test.go
+++ b/pkg/chain/chain_test.go
@@ -1,30 +1,13 @@
package chain
import (
- "fmt"
- "strings"
"testing"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource/testutil"
- "git.frostfs.info/TrueCloudLab/policy-engine/schema/common"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
- "git.frostfs.info/TrueCloudLab/policy-engine/schema/s3"
"github.com/stretchr/testify/require"
)
-func TestChainIDSerialization(t *testing.T) {
- chainIDBytes := []byte{93, 236, 80, 138, 168, 3, 144, 92, 173, 141, 16, 42, 249, 90, 97, 109, 211, 169, 54, 163}
-
- chain1 := &Chain{ID: ID(chainIDBytes)}
- data := chain1.Bytes()
-
- var chain2 Chain
- err := chain2.DecodeBytes(data)
- require.NoError(t, err)
-
- require.Equal(t, chain1.ID, chain2.ID)
-}
-
func TestEncodeDecode(t *testing.T) {
expected := Chain{
MatchType: MatchTypeFirstMatch,
@@ -52,7 +35,7 @@ func TestEncodeDecode(t *testing.T) {
require.Equal(t, expected, actual)
}
-func TestChainMatch(t *testing.T) {
+func TestReturnFirstMatch(t *testing.T) {
ch := Chain{
Rules: []Rule{
{
@@ -89,940 +72,4 @@ func TestChainMatch(t *testing.T) {
require.True(t, found)
require.Equal(t, Allow, st)
})
-
- t.Run("unknown match", func(t *testing.T) {
- ch.MatchType = MatchType(255)
- request := testutil.NewRequest(native.MethodGetObject, resource, nil)
- require.PanicsWithValue(t, "unknown MatchType 255", func() {
- ch.Match(request)
- })
- })
-
- t.Run("no rule found", func(t *testing.T) {
- ch.MatchType = MatchTypeFirstMatch
- request := testutil.NewRequest(native.MethodGetObject, resource, nil)
- st, found := ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-}
-
-func TestAnyAllConditionMatch(t *testing.T) {
- ch := Chain{
- Rules: []Rule{
- {
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{
- {
- Op: CondIPAddress,
- Kind: KindRequest,
- Key: common.PropertyKeyFrostFSSourceIP,
- Value: "192.92.1.1/20",
- },
- {
- Op: CondStringEquals,
- Kind: KindRequest,
- Key: native.PropertyKeyActorRole,
- Value: "owner",
- },
- {
- Op: CondStringEquals,
- Kind: KindResource,
- Key: native.PropertyKeyObjectID,
- Value: "79xGoKYwhyJQhrDNb7bHhY1WCvN6trHJPTjKkw24c6W9",
- },
- },
- },
- }}
-
- t.Run("match by all conditions", func(t *testing.T) {
- ch.Rules[0].Any = false
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, map[string]string{
- native.PropertyKeyObjectID: "79xGoKYwhyJQhrDNb7bHhY1WCvN6trHJPTjKkw24c6W9",
- })
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
- native.PropertyKeyActorRole: "owner",
- })
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.93.1.91",
- })
-
- st, found = ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run("match by any condition", func(t *testing.T) {
- ch.Rules[0].Any = true
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, map[string]string{
- native.PropertyKeyObjectID: "79xGoKYwhyJQhrDNb7bHhY1WCvN6trHJPTjKkw24c6W9",
- })
- request := testutil.NewRequest(native.MethodPutObject, resource, nil)
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- native.PropertyKeyActorRole: "owner",
- })
-
- st, found = ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- resource = testutil.NewResource(native.ResourceFormatRootContainers, map[string]string{})
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
- })
-
- st, found = ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.93.1.91",
- })
-
- st, found = ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-}
-
-func TestConditionMatch(t *testing.T) {
- t.Run("condition types", func(t *testing.T) {
- t.Run("slice condition type", testCondSliceContainsMatch)
- t.Run("numeric condition types", testNumericConditionsMatch)
- t.Run("string condition types", testStringConiditionsMatch)
- t.Run("ip conidition types", testIPConditionMatch)
- t.Run("unknown condition type", func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: ConditionType(255),
- Kind: KindRequest,
- Key: common.PropertyKeyFrostFSSourceIP,
- Value: "192.92.1.1/20",
- }},
- }}}
- resource := testutil.NewResource(native.ResourceFormatRootContainers, map[string]string{
- native.PropertyKeyObjectID: "79xGoKYwhyJQhrDNb7bHhY1WCvN6trHJPTjKkw24c6W9",
- })
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
- native.PropertyKeyActorRole: "owner",
- })
- require.PanicsWithValue(t, "unimplemented: 255", func() {
- ch.Match(request)
- })
- })
- })
-
- t.Run("kind", func(t *testing.T) {
- resource := testutil.NewResource(native.ResourceFormatRootContainers, map[string]string{
- native.PropertyKeyObjectID: "79xGoKYwhyJQhrDNb7bHhY1WCvN6trHJPTjKkw24c6W9",
- })
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
- native.PropertyKeyActorRole: "owner",
- })
-
- t.Run("resource", func(t *testing.T) {
- cond := Condition{
- Op: CondStringEquals,
- Kind: KindResource,
- Key: native.PropertyKeyObjectID,
- Value: "79xGoKYwhyJQhrDNb7bHhY1WCvN6trHJPTjKkw24c6W9",
- }
-
- found := cond.Match(request)
- require.True(t, found)
- })
- t.Run("request", func(t *testing.T) {
- cond := Condition{
- Op: CondStringEquals,
- Kind: KindRequest,
- Key: native.PropertyKeyActorRole,
- Value: "owner",
- }
-
- found := cond.Match(request)
- require.True(t, found)
- })
- t.Run("unknown", func(t *testing.T) {
- cond := Condition{
- Op: CondStringEquals,
- Kind: ConditionKindType(255),
- Key: native.PropertyKeyActorRole,
- Value: "owner",
- }
-
- require.PanicsWithValue(t, "unknown condition type: 255", func() {
- cond.Match(request)
- })
- })
- })
-}
-
-func testCondSliceContainsMatch(t *testing.T) {
- propKey := common.PropertyKeyFrostFSIDGroupID
- groupID := "1"
-
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondSliceContains,
- Kind: KindRequest,
- Key: propKey,
- Value: groupID,
- }},
- }}}
-
- for _, tc := range []struct {
- name string
- value string
- status Status
- }{
- {
- name: "simple value",
- value: groupID,
- status: Allow,
- },
- {
- name: "simple value by func",
- value: FormCondSliceContainsValue([]string{groupID}),
- status: Allow,
- },
- {
- name: "multiple values by func",
- value: FormCondSliceContainsValue([]string{groupID, "2", "3"}),
- status: Allow,
- },
- {
- name: "simple mismatched",
- value: "3",
- status: NoRuleFound,
- },
- {
- name: "multiple mismatched",
- value: FormCondSliceContainsValue([]string{"11", "12"}),
- status: NoRuleFound,
- },
- {
- name: "comma correct handling mismatched",
- value: "1,11",
- status: NoRuleFound,
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{propKey: tc.value})
-
- st, _ := ch.Match(request)
- require.Equal(t, tc.status.String(), st.String())
- })
- }
-}
-
-func testNumericConditionsMatch(t *testing.T) {
- propKey := s3.PropertyKeyMaxKeys
-
- for _, tc := range []struct {
- name string
- conditions []Condition
- value string
- status Status
- }{
- {
- name: "value from interval",
- conditions: []Condition{
- {
- Op: CondNumericLessThan,
- Kind: KindRequest,
- Key: propKey,
- Value: "100",
- },
- {
- Op: CondNumericGreaterThan,
- Kind: KindRequest,
- Key: propKey,
- Value: "80",
- },
- {
- Op: CondNumericNotEquals,
- Kind: KindRequest,
- Key: propKey,
- Value: "91",
- },
- },
- value: "90",
- status: Allow,
- },
- {
- name: "border value",
- conditions: []Condition{
- {
- Op: CondNumericEquals,
- Kind: KindRequest,
- Key: propKey,
- Value: "50",
- },
- {
- Op: CondNumericLessThanEquals,
- Kind: KindRequest,
- Key: propKey,
- Value: "50",
- },
- {
- Op: CondNumericGreaterThanEquals,
- Kind: KindRequest,
- Key: propKey,
- Value: "50",
- },
- },
- value: "50",
- status: Allow,
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{propKey: tc.value})
-
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: tc.conditions,
- }}}
- st, _ := ch.Match(request)
- require.Equal(t, tc.status.String(), st.String())
- })
- }
-}
-
-func testStringConiditionsMatch(t *testing.T) {
- propKey := fmt.Sprintf(common.PropertyKeyFormatFrostFSIDUserClaim, "some-tag")
- val := "tag-value"
-
- t.Run(CondStringEquals.String(), func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondStringEquals,
- Kind: KindRequest,
- Key: propKey,
- Value: val,
- }},
- }}}
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: val,
- })
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: "distort_tag_value" + val,
- })
-
- st, found = ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run(CondStringNotEquals.String(), func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondStringNotEquals,
- Kind: KindRequest,
- Key: propKey,
- Value: val,
- }},
- }}}
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: "distort_tag_value" + val,
- })
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: val,
- })
-
- st, found = ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run(CondStringEqualsIgnoreCase.String(), func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondStringEqualsIgnoreCase,
- Kind: KindRequest,
- Key: propKey,
- Value: val,
- }},
- }}}
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: strings.ToUpper(val),
- })
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: strings.ToUpper("distort_tag_value" + val),
- })
-
- st, found = ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run(CondStringNotEqualsIgnoreCase.String(), func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondStringNotEqualsIgnoreCase,
- Kind: KindRequest,
- Key: propKey,
- Value: val,
- }},
- }}}
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: strings.ToUpper("distort_tag_value" + val),
- })
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: strings.ToUpper(val),
- })
-
- st, found = ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run(CondStringLike.String(), func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondStringLike,
- Kind: KindRequest,
- Key: propKey,
- Value: val + "*",
- }},
- }}}
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: val + "suffix",
- })
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: string([]byte(val)[:len(val)-1]), //cut last letter
- })
-
- st, found = ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run(CondStringNotLike.String(), func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondStringNotLike,
- Kind: KindRequest,
- Key: propKey,
- Value: "prefix" + val + "*",
- }},
- }}}
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: val + "suffix",
- })
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: "prefix" + val,
- })
-
- st, found = ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run(CondStringLessThan.String(), func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondStringLessThan,
- Kind: KindRequest,
- Key: propKey,
- Value: val + "b",
- }},
- }}}
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: val + "a",
- })
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: val + "c",
- })
-
- st, found = ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run(CondStringLessThanEquals.String(), func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondStringLessThanEquals,
- Kind: KindRequest,
- Key: propKey,
- Value: val + "b",
- }},
- }}}
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: val + "a",
- })
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: val + "b",
- })
-
- st, found = ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
- })
-
- t.Run(CondStringGreaterThan.String(), func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondStringGreaterThan,
- Kind: KindRequest,
- Key: propKey,
- Value: val + "b",
- }},
- }}}
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: val + "c",
- })
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: val + "b",
- })
-
- st, found = ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run(CondStringGreaterThanEquals.String(), func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondStringGreaterThanEquals,
- Kind: KindRequest,
- Key: propKey,
- Value: val + "b",
- }},
- }}}
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: val + "c",
- })
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- propKey: val + "b",
- })
-
- st, found = ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
- })
-}
-
-func testIPConditionMatch(t *testing.T) {
- t.Run(CondIPAddress.String(), func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondIPAddress,
- Kind: KindRequest,
- Key: common.PropertyKeyFrostFSSourceIP,
- Value: "192.92.1.1/20",
- }},
- }}}
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
- })
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.93.1.91",
- })
-
- st, found = ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run(CondNotIPAddress.String(), func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondNotIPAddress,
- Kind: KindRequest,
- Key: common.PropertyKeyFrostFSSourceIP,
- Value: "192.92.1.1/20",
- }},
- }}}
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.93.1.91",
- })
-
- st, found := ch.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
-
- request = testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
- })
-
- st, found = ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run("invalid ip address condition value", func(t *testing.T) {
- ch := Chain{Rules: []Rule{{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondIPAddress,
- Kind: KindRequest,
- Key: common.PropertyKeyFrostFSSourceIP,
- Value: "192.92.1.1:33333",
- }},
- }}}
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
- })
-
- st, found := ch.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run("match ip", func(t *testing.T) {
- cond := Condition{
- Op: CondIPAddress,
- Kind: KindRequest,
- Key: common.PropertyKeyFrostFSSourceIP,
- Value: "192.92.1.1/10",
- }
-
- require.NotPanics(t, func() {
- cond.matchIP("192.92.1.91")
- })
-
- require.PanicsWithValue(t, "unimplemented: 10", func() {
- cond.Op = CondNumericEquals
- cond.matchIP("192.92.1.91")
- })
- })
-}
-
-func TestInvalidNumericValues(t *testing.T) {
- t.Run("invalid request property", func(t *testing.T) {
- propKey := s3.PropertyKeyMaxKeys
- propValues := []string{"", "invalid"}
-
- for _, tc := range []struct {
- name string
- conditionType ConditionType
- match bool
- }{
- {
- name: "NumericEquals condition",
- conditionType: CondNumericEquals,
- match: false,
- },
- {
- name: "NumericNotEquals condition",
- conditionType: CondNumericNotEquals,
- match: true,
- },
- {
- name: "NumericLessThan condition",
- conditionType: CondNumericLessThan,
- match: false,
- },
- {
- name: "NumericLessThanEquals condition",
- conditionType: CondNumericLessThanEquals,
- match: false,
- },
- {
- name: "NumericGreaterThan condition",
- conditionType: CondNumericGreaterThan,
- match: false,
- },
- {
- name: "NumericGreaterThanEquals condition",
- conditionType: CondNumericGreaterThanEquals,
- match: false,
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- condition := Condition{
- Op: tc.conditionType,
- Kind: KindRequest,
- Key: propKey,
- Value: "50",
- }
-
- for _, propValue := range propValues {
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{propKey: propValue})
-
- match := condition.Match(request)
- require.Equal(t, tc.match, match)
- }
- })
- }
- })
- t.Run("invalid condition numeric value", func(t *testing.T) {
- propKey := s3.PropertyKeyMaxKeys
-
- condition := Condition{
- Kind: KindRequest,
- Key: propKey,
- Value: "invalid",
- }
-
- t.Run("match on CondNumericNotEquals", func(t *testing.T) {
- condition.Op = CondNumericNotEquals
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{propKey: "50"})
-
- match := condition.Match(request)
- require.Equal(t, true, match)
- })
-
- t.Run("non-match on non-CondNumericNotEquals", func(t *testing.T) {
- condition.Op = CondNumericEquals
-
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{propKey: "50"})
-
- match := condition.Match(request)
- require.Equal(t, false, match)
- })
-
- t.Run("match numeric", func(t *testing.T) {
- cond := Condition{
- Op: CondNumericLessThan,
- Kind: KindRequest,
- Key: s3.PropertyKeyMaxKeys,
- Value: "5",
- }
-
- require.NotPanics(t, func() {
- cond.matchNumeric("6")
- })
-
- require.PanicsWithValue(t, "unimplemented: 0", func() {
- cond.Op = CondStringEquals
- cond.matchNumeric("10")
- })
- })
- })
-}
-
-func TestCondTypeStringification(t *testing.T) {
- for _, pair := range []struct {
- cond ConditionType
- expectedString string
- }{
- {CondStringEquals, "StringEquals"},
- {CondStringNotEquals, "StringNotEquals"},
- {CondStringEqualsIgnoreCase, "StringEqualsIgnoreCase"},
- {CondStringNotEqualsIgnoreCase, "StringNotEqualsIgnoreCase"},
- {CondStringLike, "StringLike"},
- {CondStringNotLike, "StringNotLike"},
- {CondStringLessThan, "StringLessThan"},
- {CondStringLessThanEquals, "StringLessThanEquals"},
- {CondStringGreaterThan, "StringGreaterThan"},
- {CondStringGreaterThanEquals, "StringGreaterThanEquals"},
- {CondNumericEquals, "NumericEquals"},
- {CondNumericNotEquals, "NumericNotEquals"},
- {CondNumericLessThan, "NumericLessThan"},
- {CondNumericLessThanEquals, "NumericLessThanEquals"},
- {CondNumericGreaterThan, "NumericGreaterThan"},
- {CondNumericGreaterThanEquals, "NumericGreaterThanEquals"},
- {CondSliceContains, "SliceContains"},
- {CondIPAddress, "IPAddress"},
- {CondNotIPAddress, "NotIPAddress"},
- {ConditionType(255), "unknown condition type"},
- } {
- require.Equal(t, pair.expectedString, pair.cond.String())
- }
-}
-
-func TestRuleMatch(t *testing.T) {
- rule := Rule{
- Status: Allow,
- Actions: Actions{Names: []string{native.MethodPutObject}},
- Resources: Resources{Names: []string{native.ResourceFormatRootContainers}},
- Condition: []Condition{{
- Op: CondIPAddress,
- Kind: KindRequest,
- Key: common.PropertyKeyFrostFSSourceIP,
- Value: "192.92.1.1/20",
- }},
- }
-
- t.Run("match", func(t *testing.T) {
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
- })
- st, found := rule.Match(request)
- require.True(t, found)
- require.Equal(t, Allow, st)
- })
-
- t.Run("not matching resource name", func(t *testing.T) {
- resource := testutil.NewResource(fmt.Sprintf(native.ResourceFormatNamespaceContainers, "namespicy"), nil)
- request := testutil.NewRequest(native.MethodPutObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
- })
- st, found := rule.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run("not matching action", func(t *testing.T) {
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodGetObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.92.1.91",
- })
- st, found := rule.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
-
- t.Run("not matching condition", func(t *testing.T) {
- resource := testutil.NewResource(native.ResourceFormatRootContainers, nil)
- request := testutil.NewRequest(native.MethodGetObject, resource, map[string]string{
- common.PropertyKeyFrostFSSourceIP: "192.93.1.91",
- })
- st, found := rule.Match(request)
- require.False(t, found)
- require.Equal(t, NoRuleFound, st)
- })
}
diff --git a/pkg/chain/marshal_binary.go b/pkg/chain/marshal_binary.go
deleted file mode 100644
index 357cc79..0000000
--- a/pkg/chain/marshal_binary.go
+++ /dev/null
@@ -1,257 +0,0 @@
-package chain
-
-import (
- "encoding"
- "fmt"
-
- "git.frostfs.info/TrueCloudLab/policy-engine/pkg/marshal"
-)
-
-const (
- ChainMarshalVersion uint8 = 0 // increase if breaking change
-)
-
-var (
- _ encoding.BinaryMarshaler = (*Chain)(nil)
- _ encoding.BinaryUnmarshaler = (*Chain)(nil)
-)
-
-func (c *Chain) MarshalBinary() ([]byte, error) {
- s := marshal.UInt8Size // Marshaller version
- s += marshal.UInt8Size // Chain version
- s += marshal.SliceSize(c.ID, func(byte) int { return marshal.ByteSize })
- s += marshal.SliceSize(c.Rules, ruleSize)
- s += marshal.UInt8Size // MatchType
-
- buf := make([]byte, s)
- var offset int
- var err error
- offset, err = marshal.UInt8Marshal(buf, offset, marshal.Version)
- if err != nil {
- return nil, err
- }
- offset, err = marshal.UInt8Marshal(buf, offset, ChainMarshalVersion)
- if err != nil {
- return nil, err
- }
- offset, err = marshal.SliceMarshal(buf, offset, c.ID, marshal.ByteMarshal)
- if err != nil {
- return nil, err
- }
- offset, err = marshal.SliceMarshal(buf, offset, c.Rules, marshalRule)
- if err != nil {
- return nil, err
- }
- offset, err = marshal.UInt8Marshal(buf, offset, uint8(c.MatchType))
- if err != nil {
- return nil, err
- }
-
- if err := marshal.VerifyMarshal(buf, offset); err != nil {
- return nil, err
- }
- return buf, nil
-}
-
-func (c *Chain) UnmarshalBinary(data []byte) error {
- var offset int
-
- marshallerVersion, offset, err := marshal.UInt8Unmarshal(data, offset)
- if err != nil {
- return err
- }
- if marshallerVersion != marshal.Version {
- return fmt.Errorf("unsupported marshaller version %d", marshallerVersion)
- }
-
- chainVersion, offset, err := marshal.UInt8Unmarshal(data, offset)
- if err != nil {
- return err
- }
- if chainVersion != ChainMarshalVersion {
- return fmt.Errorf("unsupported chain version %d", chainVersion)
- }
-
- idBytes, offset, err := marshal.SliceUnmarshal(data, offset, marshal.ByteUnmarshal)
- if err != nil {
- return err
- }
- c.ID = ID(idBytes)
-
- c.Rules, offset, err = marshal.SliceUnmarshal(data, offset, unmarshalRule)
- if err != nil {
- return err
- }
-
- matchTypeV, offset, err := marshal.UInt8Unmarshal(data, offset)
- if err != nil {
- return err
- }
- c.MatchType = MatchType(matchTypeV)
-
- return marshal.VerifyUnmarshal(data, offset)
-}
-
-func ruleSize(r Rule) int {
- s := marshal.ByteSize // Status
- s += actionsSize(r.Actions)
- s += resourcesSize(r.Resources)
- s += marshal.BoolSize // Any
- s += marshal.SliceSize(r.Condition, conditionSize)
- return s
-}
-
-func marshalRule(buf []byte, offset int, r Rule) (int, error) {
- offset, err := marshal.ByteMarshal(buf, offset, byte(r.Status))
- if err != nil {
- return 0, err
- }
- offset, err = marshalActions(buf, offset, r.Actions)
- if err != nil {
- return 0, err
- }
- offset, err = marshalResources(buf, offset, r.Resources)
- if err != nil {
- return 0, err
- }
- offset, err = marshal.BoolMarshal(buf, offset, r.Any)
- if err != nil {
- return 0, err
- }
- return marshal.SliceMarshal(buf, offset, r.Condition, marshalCondition)
-}
-
-func unmarshalRule(buf []byte, offset int) (Rule, int, error) {
- var r Rule
- statusV, offset, err := marshal.ByteUnmarshal(buf, offset)
- if err != nil {
- return Rule{}, 0, err
- }
- r.Status = Status(statusV)
-
- r.Actions, offset, err = unmarshalActions(buf, offset)
- if err != nil {
- return Rule{}, 0, err
- }
-
- r.Resources, offset, err = unmarshalResources(buf, offset)
- if err != nil {
- return Rule{}, 0, err
- }
-
- r.Any, offset, err = marshal.BoolUnmarshal(buf, offset)
- if err != nil {
- return Rule{}, 0, err
- }
-
- r.Condition, offset, err = marshal.SliceUnmarshal(buf, offset, unmarshalCondition)
- if err != nil {
- return Rule{}, 0, err
- }
-
- return r, offset, nil
-}
-
-func actionsSize(a Actions) int {
- return marshal.BoolSize + // Inverted
- marshal.SliceSize(a.Names, marshal.StringSize)
-}
-
-func marshalActions(buf []byte, offset int, a Actions) (int, error) {
- offset, err := marshal.BoolMarshal(buf, offset, a.Inverted)
- if err != nil {
- return 0, err
- }
- return marshal.SliceMarshal(buf, offset, a.Names, marshal.StringMarshal)
-}
-
-func unmarshalActions(buf []byte, offset int) (Actions, int, error) {
- var a Actions
- var err error
- a.Inverted, offset, err = marshal.BoolUnmarshal(buf, offset)
- if err != nil {
- return Actions{}, 0, err
- }
- a.Names, offset, err = marshal.SliceUnmarshal(buf, offset, marshal.StringUnmarshal)
- if err != nil {
- return Actions{}, 0, err
- }
- return a, offset, nil
-}
-
-func resourcesSize(r Resources) int {
- return marshal.BoolSize + // Inverted
- marshal.SliceSize(r.Names, marshal.StringSize)
-}
-
-func marshalResources(buf []byte, offset int, r Resources) (int, error) {
- offset, err := marshal.BoolMarshal(buf, offset, r.Inverted)
- if err != nil {
- return 0, err
- }
- return marshal.SliceMarshal(buf, offset, r.Names, marshal.StringMarshal)
-}
-
-func unmarshalResources(buf []byte, offset int) (Resources, int, error) {
- var r Resources
- var err error
- r.Inverted, offset, err = marshal.BoolUnmarshal(buf, offset)
- if err != nil {
- return Resources{}, 0, err
- }
- r.Names, offset, err = marshal.SliceUnmarshal(buf, offset, marshal.StringUnmarshal)
- if err != nil {
- return Resources{}, 0, err
- }
- return r, offset, nil
-}
-
-func conditionSize(c Condition) int {
- return marshal.ByteSize + // Op
- marshal.ByteSize + // Object
- marshal.StringSize(c.Key) +
- marshal.StringSize(c.Value)
-}
-
-func marshalCondition(buf []byte, offset int, c Condition) (int, error) {
- offset, err := marshal.ByteMarshal(buf, offset, byte(c.Op))
- if err != nil {
- return 0, err
- }
- offset, err = marshal.ByteMarshal(buf, offset, byte(c.Kind))
- if err != nil {
- return 0, err
- }
- offset, err = marshal.StringMarshal(buf, offset, c.Key)
- if err != nil {
- return 0, err
- }
- return marshal.StringMarshal(buf, offset, c.Value)
-}
-
-func unmarshalCondition(buf []byte, offset int) (Condition, int, error) {
- var c Condition
- opV, offset, err := marshal.ByteUnmarshal(buf, offset)
- if err != nil {
- return Condition{}, 0, err
- }
- c.Op = ConditionType(opV)
-
- obV, offset, err := marshal.ByteUnmarshal(buf, offset)
- if err != nil {
- return Condition{}, 0, err
- }
- c.Kind = ConditionKindType(obV)
-
- c.Key, offset, err = marshal.StringUnmarshal(buf, offset)
- if err != nil {
- return Condition{}, 0, err
- }
-
- c.Value, offset, err = marshal.StringUnmarshal(buf, offset)
- if err != nil {
- return Condition{}, 0, err
- }
-
- return c, offset, nil
-}
diff --git a/pkg/chain/marshal_binary_test.go b/pkg/chain/marshal_binary_test.go
deleted file mode 100644
index 4cfd0cb..0000000
--- a/pkg/chain/marshal_binary_test.go
+++ /dev/null
@@ -1,247 +0,0 @@
-package chain
-
-import (
- "fmt"
- "testing"
-
- "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
- "github.com/google/uuid"
- "github.com/stretchr/testify/require"
-)
-
-func TestChainMarshalling(t *testing.T) {
- t.Parallel()
- for _, id := range generateTestIDs() {
- for _, rules := range generateTestRules() {
- for _, matchType := range generateTestMatchTypes() {
- performMarshalTest(t, id, rules, matchType)
- }
- }
- }
-}
-
-func TestInvalidChainData(t *testing.T) {
- var ch Chain
- require.Error(t, ch.UnmarshalBinary(nil))
- require.Error(t, ch.UnmarshalBinary([]byte{}))
- require.Error(t, ch.UnmarshalBinary([]byte{1, 2, 3}))
- require.Error(t, ch.UnmarshalBinary([]byte("\x00\x00:aws:iam::namespace:group/so\x82\x82\x82\x82\x82\x82u\x82")))
-}
-
-func performMarshalTest(t *testing.T, id ID, r []Rule, mt MatchType) {
- chain := Chain{
- ID: id,
- Rules: r,
- MatchType: mt,
- }
- data, err := chain.MarshalBinary()
- require.NoError(t, err)
-
- var unmarshalledChain Chain
- require.NoError(t, unmarshalledChain.UnmarshalBinary(data))
-
- require.Equal(t, chain, unmarshalledChain)
-}
-
-func generateTestIDs() []ID {
- return []ID{
- ID(""),
- ID(uuid.New().String()),
- ID("*::/"),
- ID("avada kedavra"),
- ID("arn:aws:iam::namespace:group/some_group"),
- ID("$Object:homomorphicHash"),
- ID("native:container/ns/9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J"),
- }
-}
-
-func generateTestRules() [][]Rule {
- result := [][]Rule{
- nil,
- {},
- {},
- }
-
- for _, st := range generateTestStatuses() {
- for _, act := range generateTestActions() {
- for _, res := range generateTestResources() {
- for _, cond := range generateTestConditions() {
- result[2] = append(result[2], Rule{
- Status: st,
- Actions: act,
- Resources: res,
- Condition: cond,
- Any: true,
- })
- result[2] = append(result[2], Rule{
- Status: st,
- Actions: act,
- Resources: res,
- Condition: cond,
- })
- }
- }
- }
- }
-
- return result
-}
-
-func generateTestStatuses() []Status {
- return []Status{
- Allow,
- NoRuleFound,
- AccessDenied,
- QuotaLimitReached,
- }
-}
-
-func generateTestActions() []Actions {
- return []Actions{
- {
- Inverted: true,
- Names: nil,
- },
- {
- Names: nil,
- },
- {
- Inverted: true,
- Names: []string{},
- },
- {
- Names: []string{},
- },
- {
- Inverted: true,
- Names: []string{native.MethodPutObject},
- },
- {
- Names: []string{native.MethodPutObject},
- },
- {
- Inverted: true,
- Names: []string{native.MethodPutObject, native.MethodDeleteContainer, native.MethodDeleteObject},
- },
- {
- Names: []string{native.MethodPutObject, native.MethodDeleteContainer, native.MethodDeleteObject},
- },
- }
-}
-
-func generateTestResources() []Resources {
- return []Resources{
- {
- Inverted: true,
- Names: nil,
- },
- {
- Names: nil,
- },
- {
- Inverted: true,
- Names: []string{},
- },
- {
- Names: []string{},
- },
- {
- Inverted: true,
- Names: []string{native.ResourceFormatAllObjects},
- },
- {
- Names: []string{native.ResourceFormatAllObjects},
- },
- {
- Inverted: true,
- Names: []string{
- native.ResourceFormatAllObjects,
- fmt.Sprintf(native.ResourceFormatRootContainer, "9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J"),
- },
- },
- {
- Names: []string{
- native.ResourceFormatAllObjects,
- fmt.Sprintf(native.ResourceFormatRootContainer, "9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J"),
- },
- },
- }
-}
-
-func generateTestConditions() [][]Condition {
- result := [][]Condition{
- nil,
- {},
- {},
- }
-
- for _, ct := range generateTestConditionTypes() {
- for _, ot := range generateObjectTypes() {
- result[2] = append(result[2], Condition{
- Op: ct,
- Kind: ot,
- Key: "",
- Value: "",
- })
-
- result[2] = append(result[2], Condition{
- Op: ct,
- Kind: ot,
- Key: "key",
- Value: "",
- })
-
- result[2] = append(result[2], Condition{
- Op: ct,
- Kind: ot,
- Key: "",
- Value: "value",
- })
-
- result[2] = append(result[2], Condition{
- Op: ct,
- Kind: ot,
- Key: "key",
- Value: "value",
- })
- }
- }
-
- return result
-}
-
-func generateTestConditionTypes() []ConditionType {
- return []ConditionType{
- CondStringEquals,
- CondStringNotEquals,
- CondStringEqualsIgnoreCase,
- CondStringNotEqualsIgnoreCase,
- CondStringLike,
- CondStringNotLike,
- CondStringLessThan,
- CondStringLessThanEquals,
- CondStringGreaterThan,
- CondStringGreaterThanEquals,
- CondNumericEquals,
- CondNumericNotEquals,
- CondNumericLessThan,
- CondNumericLessThanEquals,
- CondNumericGreaterThan,
- CondNumericGreaterThanEquals,
- CondSliceContains,
- }
-}
-
-func generateObjectTypes() []ConditionKindType {
- return []ConditionKindType{
- KindResource,
- KindRequest,
- }
-}
-
-func generateTestMatchTypes() []MatchType {
- return []MatchType{
- MatchTypeDenyPriority,
- MatchTypeFirstMatch,
- }
-}
diff --git a/pkg/chain/marshal_fuzz.go b/pkg/chain/marshal_fuzz.go
deleted file mode 100644
index b5cbec9..0000000
--- a/pkg/chain/marshal_fuzz.go
+++ /dev/null
@@ -1,13 +0,0 @@
-//go:build gofuzz
-// +build gofuzz
-
-package chain
-
-func DoFuzzChainUnmarshalBinary(data []byte) int {
- var ch Chain
- err := ch.UnmarshalBinary(data)
- if err != nil {
- return 0
- }
- return 1
-}
diff --git a/pkg/chain/marshal_fuzz_test.go b/pkg/chain/marshal_fuzz_test.go
deleted file mode 100644
index f6956c5..0000000
--- a/pkg/chain/marshal_fuzz_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-//go:build gofuzz
-// +build gofuzz
-
-package chain
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func FuzzUnmarshal(f *testing.F) {
- for _, id := range generateTestIDs() {
- for _, rules := range generateTestRules() {
- for _, matchType := range generateTestMatchTypes() {
-
- chain := Chain{
- ID: id,
- Rules: rules,
- MatchType: matchType,
- }
- data, err := chain.MarshalBinary()
- require.NoError(f, err)
- f.Add(data)
- }
- }
- }
-
- f.Fuzz(func(t *testing.T, data []byte) {
- require.NotPanics(t, func() {
- DoFuzzChainUnmarshalBinary(data)
- })
- })
-}
diff --git a/pkg/chain/marshal_json.go b/pkg/chain/marshal_json.go
deleted file mode 100644
index a83c816..0000000
--- a/pkg/chain/marshal_json.go
+++ /dev/null
@@ -1,145 +0,0 @@
-package chain
-
-import (
- "fmt"
- "strconv"
-
- jlexer "github.com/mailru/easyjson/jlexer"
- jwriter "github.com/mailru/easyjson/jwriter"
-)
-
-// Run `make generate`` if types added or changed
-
-var matchTypeToJSONValue = []struct {
- mt MatchType
- str string
-}{
- {MatchTypeDenyPriority, "DenyPriority"},
- {MatchTypeFirstMatch, "FirstMatch"},
-}
-
-var statusToJSONValue = []struct {
- s Status
- str string
-}{
- {Allow, "Allow"},
- {NoRuleFound, "NoRuleFound"},
- {AccessDenied, "AccessDenied"},
- {QuotaLimitReached, "QuotaLimitReached"},
-}
-
-var objectTypeToJSONValue = []struct {
- t ConditionKindType
- str string
-}{
- {KindRequest, "Request"},
- {KindResource, "Resource"},
-}
-
-func (mt MatchType) MarshalEasyJSON(w *jwriter.Writer) {
- for _, p := range matchTypeToJSONValue {
- if p.mt == mt {
- w.String(p.str)
- return
- }
- }
- w.String(strconv.FormatUint(uint64(mt), 10))
-}
-
-func (mt *MatchType) UnmarshalEasyJSON(l *jlexer.Lexer) {
- str := l.String()
- for _, p := range matchTypeToJSONValue {
- if p.str == str {
- *mt = p.mt
- return
- }
- }
-
- v, err := strconv.ParseUint(str, 10, 8)
- if err != nil {
- l.AddError(fmt.Errorf("failed to parse match type: %w", err))
- return
- }
- *mt = MatchType(v)
-}
-
-func (st Status) MarshalEasyJSON(w *jwriter.Writer) {
- for _, p := range statusToJSONValue {
- if p.s == st {
- w.String(p.str)
- return
- }
- }
- w.String(strconv.FormatUint(uint64(st), 10))
-}
-
-func (st *Status) UnmarshalEasyJSON(l *jlexer.Lexer) {
- str := l.String()
- for _, p := range statusToJSONValue {
- if p.str == str {
- *st = p.s
- return
- }
- }
-
- v, err := strconv.ParseUint(str, 10, 8)
- if err != nil {
- l.AddError(fmt.Errorf("failed to parse status: %w", err))
- return
- }
- *st = Status(v)
-}
-
-func (ot ConditionKindType) MarshalEasyJSON(w *jwriter.Writer) {
- for _, p := range objectTypeToJSONValue {
- if p.t == ot {
- w.String(p.str)
- return
- }
- }
- w.String(strconv.FormatUint(uint64(ot), 10))
-}
-
-func (ot *ConditionKindType) UnmarshalEasyJSON(l *jlexer.Lexer) {
- str := l.String()
- for _, p := range objectTypeToJSONValue {
- if p.str == str {
- *ot = p.t
- return
- }
- }
-
- v, err := strconv.ParseUint(str, 10, 8)
- if err != nil {
- l.AddError(fmt.Errorf("failed to parse object type: %w", err))
- return
- }
- *ot = ConditionKindType(v)
-}
-
-func (ct ConditionType) MarshalEasyJSON(w *jwriter.Writer) {
- for _, p := range condToStr {
- if p.ct == ct {
- w.String(p.str)
- return
- }
- }
- w.String(strconv.FormatUint(uint64(ct), 10))
-}
-
-func (ct *ConditionType) UnmarshalEasyJSON(l *jlexer.Lexer) {
- str := l.String()
- for _, p := range condToStr {
- if p.str == str {
- *ct = p.ct
- return
- }
- }
-
- v, err := strconv.ParseUint(str, 10, 8)
- if err != nil {
- l.AddError(fmt.Errorf("failed to parse condition type: %w", err))
- return
- }
- *ct = ConditionType(v)
-}
diff --git a/pkg/chain/marshal_json_test.go b/pkg/chain/marshal_json_test.go
deleted file mode 100644
index c4bc621..0000000
--- a/pkg/chain/marshal_json_test.go
+++ /dev/null
@@ -1,121 +0,0 @@
-package chain
-
-import (
- "fmt"
- "os"
- "testing"
-
- "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
- "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
- "github.com/stretchr/testify/require"
-)
-
-func TestID(t *testing.T) {
- key, err := keys.NewPrivateKeyFromWIF("L5eVx6HcHaFpQpvjQ3fy29uKDZ8rQ34bfMVx4XfZMm52EqafpNMg") // s3-gw key
- require.NoError(t, err)
-
- chain1 := &Chain{ID: ID(key.PublicKey().GetScriptHash().BytesBE())}
- data := chain1.Bytes()
-
- var chain2 Chain
- require.NoError(t, chain2.DecodeBytes(data))
-
- require.Equal(t, chain1.ID, chain2.ID)
-
- data, err = chain1.MarshalJSON()
- require.NoError(t, err)
-
- require.NoError(t, chain2.UnmarshalJSON(data))
-
- require.Equal(t, chain1.ID, chain2.ID)
-}
-
-func TestMatchTypeJson(t *testing.T) {
- for _, mt := range []MatchType{MatchTypeDenyPriority, MatchTypeFirstMatch, MatchType(100)} {
- var chain Chain
- chain.MatchType = mt
-
- data, err := chain.MarshalJSON()
- require.NoError(t, err)
- if mt == MatchTypeDenyPriority {
- require.Equal(t, []byte("{\"ID\":null,\"Rules\":null,\"MatchType\":\"DenyPriority\"}"), data)
- } else if mt == MatchTypeFirstMatch {
- require.Equal(t, []byte("{\"ID\":null,\"Rules\":null,\"MatchType\":\"FirstMatch\"}"), data)
- } else {
- require.Equal(t, []byte(fmt.Sprintf("{\"ID\":null,\"Rules\":null,\"MatchType\":\"%d\"}", mt)), data)
- }
-
- var parsed Chain
- require.NoError(t, parsed.UnmarshalJSON(data))
- require.Equal(t, chain, parsed)
-
- require.Error(t, parsed.UnmarshalJSON([]byte("{\"ID\":\"\",\"Rules\":null,\"MatchType\":\"NotValid\"}")))
- }
-}
-
-func TestJsonEnums(t *testing.T) {
- chain := Chain{
- ID: []byte("2cca5ae7-cee8-428d-b45f-567fb1d03f01"), // will be encoded to base64
- MatchType: MatchTypeFirstMatch,
- Rules: []Rule{
- {
- Status: AccessDenied,
- Actions: Actions{
- Names: []string{native.MethodDeleteObject, native.MethodGetContainer},
- },
- Resources: Resources{
- Names: []string{native.ResourceFormatAllObjects},
- },
- Condition: []Condition{
- {
- Op: CondStringEquals,
- Kind: KindRequest,
- Key: native.PropertyKeyActorRole,
- Value: native.PropertyValueContainerRoleOthers,
- },
- },
- },
- {
- Status: QuotaLimitReached,
- Actions: Actions{
- Inverted: true,
- Names: []string{native.MethodPutObject},
- },
- Resources: Resources{
- Names: []string{fmt.Sprintf(native.ResourceFormatRootContainerObjects, "9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J")},
- },
- Any: true,
- Condition: []Condition{
- {
- Op: CondStringNotLike,
- Kind: KindResource,
- Key: native.PropertyKeyObjectType,
- Value: "regular",
- },
- },
- },
- {
- Status: Status(100),
- Condition: []Condition{
- {
- Op: ConditionType(255),
- Kind: ConditionKindType(128),
- },
- },
- },
- },
- }
-
- data, err := chain.MarshalJSON()
- require.NoError(t, err)
-
- var parsed Chain
- require.NoError(t, parsed.UnmarshalJSON(data))
- require.Equal(t, chain, parsed)
-
- expected, err := os.ReadFile("./testdata/test_status_json.json")
- require.NoError(t, err)
-
- require.NoError(t, parsed.UnmarshalJSON(expected))
- require.Equal(t, chain, parsed)
-}
diff --git a/pkg/chain/testdata/test_status_json.json b/pkg/chain/testdata/test_status_json.json
deleted file mode 100644
index 6955ce7..0000000
--- a/pkg/chain/testdata/test_status_json.json
+++ /dev/null
@@ -1,75 +0,0 @@
-{
- "ID": "MmNjYTVhZTctY2VlOC00MjhkLWI0NWYtNTY3ZmIxZDAzZjAx",
- "Rules": [
- {
- "Status": "AccessDenied",
- "Actions": {
- "Inverted": false,
- "Names": [
- "DeleteObject",
- "GetContainer"
- ]
- },
- "Resources": {
- "Inverted": false,
- "Names": [
- "native:object/*"
- ]
- },
- "Any": false,
- "Condition": [
- {
- "Op": "StringEquals",
- "Kind": "Request",
- "Key": "$Actor:role",
- "Value": "others"
- }
- ]
- },
- {
- "Status": "QuotaLimitReached",
- "Actions": {
- "Inverted": true,
- "Names": [
- "PutObject"
- ]
- },
- "Resources": {
- "Inverted": false,
- "Names": [
- "native:object//9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J/*"
- ]
- },
- "Any": true,
- "Condition": [
- {
- "Op": "StringNotLike",
- "Kind": "Resource",
- "Key": "$Object:objectType",
- "Value": "regular"
- }
- ]
- },
- {
- "Status": "100",
- "Actions": {
- "Inverted": false,
- "Names": null
- },
- "Resources": {
- "Inverted": false,
- "Names": null
- },
- "Any": false,
- "Condition": [
- {
- "Op": "255",
- "Kind": "128",
- "Key": "",
- "Value": ""
- }
- ]
- }
- ],
- "MatchType": "FirstMatch"
-}
diff --git a/pkg/engine/chain_router.go b/pkg/engine/chain_router.go
index 3eced4a..93775b8 100644
--- a/pkg/engine/chain_router.go
+++ b/pkg/engine/chain_router.go
@@ -6,18 +6,18 @@ import (
)
type defaultChainRouter struct {
- morph MorphRuleChainStorageReader
+ morph MorphRuleChainStorage
local LocalOverrideStorage
}
-func NewDefaultChainRouter(morph MorphRuleChainStorageReader) ChainRouter {
+func NewDefaultChainRouter(morph MorphRuleChainStorage) ChainRouter {
return &defaultChainRouter{
morph: morph,
}
}
-func NewDefaultChainRouterWithLocalOverrides(morph MorphRuleChainStorageReader, local LocalOverrideStorage) ChainRouter {
+func NewDefaultChainRouterWithLocalOverrides(morph MorphRuleChainStorage, local LocalOverrideStorage) ChainRouter {
return &defaultChainRouter{
morph: morph,
local: local,
@@ -42,36 +42,43 @@ func (dr *defaultChainRouter) checkLocal(name chain.Name, rt RequestTarget, r re
if dr.local == nil {
return
}
-
- var hasAllow bool
+ var ruleFounds []bool
for _, target := range rt.Targets() {
status, ruleFound, err = dr.matchLocalOverrides(name, target, r)
if err != nil || ruleFound && status != chain.Allow {
return
}
- hasAllow = hasAllow || ruleFound
+ ruleFounds = append(ruleFounds, ruleFound)
}
- if hasAllow {
- return chain.Allow, true, nil
+ status = chain.NoRuleFound
+ for _, ruleFound = range ruleFounds {
+ if ruleFound {
+ status = chain.Allow
+ break
+ }
}
- return chain.NoRuleFound, false, nil
+ return
}
func (dr *defaultChainRouter) checkMorph(name chain.Name, rt RequestTarget, r resource.Request) (status chain.Status, ruleFound bool, err error) {
- var hasAllow bool
+ var ruleFounds []bool
for _, target := range rt.Targets() {
status, ruleFound, err = dr.matchMorphRuleChains(name, target, r)
if err != nil || ruleFound && status != chain.Allow {
return
}
- hasAllow = hasAllow || ruleFound
+ ruleFounds = append(ruleFounds, ruleFound)
}
- if hasAllow {
- return chain.Allow, true, nil
+ status = chain.NoRuleFound
+ for _, ruleFound = range ruleFounds {
+ if ruleFound {
+ status = chain.Allow
+ break
+ }
}
- return chain.NoRuleFound, false, nil
+ return
}
func (dr *defaultChainRouter) matchLocalOverrides(name chain.Name, target Target, r resource.Request) (status chain.Status, ruleFound bool, err error) {
diff --git a/pkg/engine/inmemory/inmemory_test.go b/pkg/engine/inmemory/inmemory_test.go
index 30bd7c3..1706c2a 100644
--- a/pkg/engine/inmemory/inmemory_test.go
+++ b/pkg/engine/inmemory/inmemory_test.go
@@ -1,79 +1,14 @@
package inmemory
import (
- "bytes"
"testing"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
resourcetest "git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource/testutil"
- "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
-func TestAddRootOverrides(t *testing.T) {
- s := NewInMemoryLocalOverrides()
-
- target := engine.NamespaceTarget("")
-
- id, err := s.LocalStorage().AddOverride(chain.S3, target, &chain.Chain{
- Rules: []chain.Rule{{
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{"s3:PutObject"}},
- Resources: chain.Resources{Names: []string{"*"}},
- }},
- })
- require.NoError(t, err)
-
- res, err := s.LocalStorage().ListOverrides(chain.S3, target)
- require.NoError(t, err)
- require.Len(t, res, 1)
- require.Equal(t, string(id), string(res[0].ID))
-}
-
-func TestInmemory_MultipleTargets(t *testing.T) {
- const op = "ape::test::op"
-
- targets := []engine.Target{
- engine.NamespaceTarget("ns1"),
- engine.ContainerTarget("cnr1"),
- engine.GroupTarget("group1"),
- engine.UserTarget("user1"),
- }
- req := resourcetest.NewRequest(op, resourcetest.NewResource("r", nil), nil)
- target := engine.NewRequestTargetExtended("ns1", "cnr1", "user1", []string{"group1"})
- for _, tt := range targets {
- t.Run("morph", func(t *testing.T) {
- s := NewInMemoryLocalOverrides()
- s.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, tt, &chain.Chain{
- Rules: []chain.Rule{{
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{op}},
- }},
- })
-
- status, found, err := s.IsAllowed(chain.Ingress, target, req)
- require.NoError(t, err)
- require.True(t, found)
- require.Equal(t, chain.Allow, status)
- })
- t.Run("override", func(t *testing.T) {
- s := NewInMemoryLocalOverrides()
- s.LocalStorage().AddOverride(chain.Ingress, tt, &chain.Chain{
- Rules: []chain.Rule{{
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{op}},
- }},
- })
-
- status, found, err := s.IsAllowed(chain.Ingress, target, req)
- require.NoError(t, err)
- require.True(t, found)
- require.Equal(t, chain.Allow, status)
- })
- }
-}
-
func TestInmemory(t *testing.T) {
const (
object = "native::object::abc/xyz"
@@ -112,26 +47,22 @@ func TestInmemory(t *testing.T) {
Any: true,
Condition: []chain.Condition{
{
- Op: chain.CondStringNotLike,
- Kind: chain.KindRequest,
- Key: "SourceIP",
- Value: "10.1.1.*",
+ Op: chain.CondStringNotLike,
+ Object: chain.ObjectRequest,
+ Key: "SourceIP",
+ Value: "10.1.1.*",
},
{
- Op: chain.CondStringNotEquals,
- Kind: chain.KindRequest,
- Key: "Actor",
- Value: actor1,
+ Op: chain.CondStringNotEquals,
+ Object: chain.ObjectRequest,
+ Key: "Actor",
+ Value: actor1,
},
},
},
},
})
- _, it, err := s.MorphRuleChainStorage().ListTargetsIterator(engine.Namespace)
- require.NoError(t, err)
- itemStacksEqual(t, it.Values, toStackItems(namespace))
-
s.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(namespace2), &chain.Chain{
Rules: []chain.Rule{
{ // Deny all expect "native::object::get" for all objects expect "native::object::abc/xyz".
@@ -142,10 +73,6 @@ func TestInmemory(t *testing.T) {
},
})
- _, it, err = s.MorphRuleChainStorage().ListTargetsIterator(engine.Namespace)
- require.NoError(t, err)
- itemStacksEqual(t, it.Values, toStackItems(namespace, namespace2))
-
s.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.ContainerTarget(container), &chain.Chain{
Rules: []chain.Rule{
{ // Allow to actor2 to get objects from the specific container only if they have `Department=HR` attribute.
@@ -154,30 +81,22 @@ func TestInmemory(t *testing.T) {
Resources: chain.Resources{Names: []string{"native::object::abc/*"}},
Condition: []chain.Condition{
{
- Op: chain.CondStringEquals,
- Kind: chain.KindResource,
- Key: "Department",
- Value: "HR",
+ Op: chain.CondStringEquals,
+ Object: chain.ObjectResource,
+ Key: "Department",
+ Value: "HR",
},
{
- Op: chain.CondStringEquals,
- Kind: chain.KindRequest,
- Key: "Actor",
- Value: actor2,
+ Op: chain.CondStringEquals,
+ Object: chain.ObjectRequest,
+ Key: "Actor",
+ Value: actor2,
},
},
},
},
})
- _, it, err = s.MorphRuleChainStorage().ListTargetsIterator(engine.Namespace)
- require.NoError(t, err)
- itemStacksEqual(t, it.Values, toStackItems(namespace, namespace2))
-
- _, it, err = s.MorphRuleChainStorage().ListTargetsIterator(engine.Container)
- require.NoError(t, err)
- itemStacksEqual(t, it.Values, toStackItems(container))
-
t.Run("bad subnet, namespace deny", func(t *testing.T) {
// Request initiating from the untrusted subnet.
reqBadIP := resourcetest.NewRequest("native::object::put", res, map[string]string{
@@ -256,14 +175,6 @@ func TestInmemory(t *testing.T) {
}},
})
- _, it, err = s.MorphRuleChainStorage().ListTargetsIterator(engine.Namespace)
- require.NoError(t, err)
- itemStacksEqual(t, it.Values, toStackItems(namespace, namespace2))
-
- _, it, err = s.MorphRuleChainStorage().ListTargetsIterator(engine.Container)
- require.NoError(t, err)
- itemStacksEqual(t, it.Values, toStackItems(container))
-
status, ok, _ = s.IsAllowed(chain.Ingress, engine.NewRequestTarget(namespace, container), reqGood)
require.Equal(t, chain.NoRuleFound, status)
require.False(t, ok)
@@ -279,14 +190,6 @@ func TestInmemory(t *testing.T) {
}},
})
- _, it, err = s.MorphRuleChainStorage().ListTargetsIterator(engine.Namespace)
- require.NoError(t, err)
- itemStacksEqual(t, it.Values, toStackItems(namespace, namespace2))
-
- _, it, err = s.MorphRuleChainStorage().ListTargetsIterator(engine.Container)
- require.NoError(t, err)
- itemStacksEqual(t, it.Values, toStackItems(container))
-
status, ok, _ = s.IsAllowed(chain.Ingress, engine.NewRequestTarget(namespace, container), reqGood)
require.Equal(t, chain.QuotaLimitReached, status)
require.True(t, ok)
@@ -295,83 +198,9 @@ func TestInmemory(t *testing.T) {
err := s.LocalStorage().RemoveOverride(chain.Ingress, engine.ContainerTarget(container), quotaRuleChainID)
require.NoError(t, err)
- _, it, err = s.MorphRuleChainStorage().ListTargetsIterator(engine.Namespace)
- require.NoError(t, err)
- itemStacksEqual(t, it.Values, toStackItems(namespace, namespace2))
-
- _, it, err = s.MorphRuleChainStorage().ListTargetsIterator(engine.Container)
- require.NoError(t, err)
- itemStacksEqual(t, it.Values, toStackItems(container))
-
status, ok, _ = s.IsAllowed(chain.Ingress, engine.NewRequestTarget(namespace, container), reqGood)
require.Equal(t, chain.NoRuleFound, status)
require.False(t, ok)
})
})
-
- t.Run("remove all", func(t *testing.T) {
- s := NewInMemoryLocalOverrides()
- _, _, err := s.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(namespace), &chain.Chain{
- Rules: []chain.Rule{
- {
- Status: chain.AccessDenied,
- Actions: chain.Actions{Inverted: true, Names: []string{"native::object::get"}},
- Resources: chain.Resources{Inverted: true, Names: []string{object}},
- },
- },
- })
- require.NoError(t, err)
- _, _, err = s.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(namespace2), &chain.Chain{
- Rules: []chain.Rule{
- {
- Status: chain.Allow,
- Actions: chain.Actions{Inverted: true, Names: []string{"native::object::get"}},
- Resources: chain.Resources{Inverted: true, Names: []string{object}},
- },
- },
- })
- require.NoError(t, err)
- _, _, err = s.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.NamespaceTarget(namespace2), &chain.Chain{
- Rules: []chain.Rule{
- {
- Status: chain.AccessDenied,
- Actions: chain.Actions{Inverted: true, Names: []string{"native::object::get"}},
- Resources: chain.Resources{Inverted: true, Names: []string{object}},
- },
- },
- })
- require.NoError(t, err)
- _, _, err = s.MorphRuleChainStorage().RemoveMorphRuleChainsByTarget(chain.Ingress, engine.NamespaceTarget(namespace2))
- require.NoError(t, err)
- chains, err := s.MorphRuleChainStorage().ListMorphRuleChains(chain.Ingress, engine.NamespaceTarget(namespace2))
- require.NoError(t, err)
- require.Equal(t, 0, len(chains))
- chains, err = s.MorphRuleChainStorage().ListMorphRuleChains(chain.Ingress, engine.NamespaceTarget(namespace))
- require.NoError(t, err)
- require.Equal(t, 1, len(chains))
- })
-}
-
-func itemStacksEqual(t *testing.T, got []stackitem.Item, expected []stackitem.Item) {
-next:
- for _, exp := range expected {
- expBytes, err := exp.TryBytes()
- require.NoError(t, err)
- for _, v := range got {
- vBytes, err := v.TryBytes()
- require.NoError(t, err)
- if bytes.Equal(vBytes, expBytes) {
- continue next
- }
- }
- t.Fatalf("not found %s", exp)
- }
-}
-
-func toStackItems(names ...string) []stackitem.Item {
- var items []stackitem.Item
- for _, name := range names {
- items = append(items, stackitem.NewByteArray([]byte(name)))
- }
- return items
}
diff --git a/pkg/engine/inmemory/local_storage.go b/pkg/engine/inmemory/local_storage.go
index b585816..4055657 100644
--- a/pkg/engine/inmemory/local_storage.go
+++ b/pkg/engine/inmemory/local_storage.go
@@ -1,11 +1,9 @@
package inmemory
import (
- "bytes"
"fmt"
"math/rand"
"strings"
- "sync"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
@@ -15,16 +13,14 @@ import (
type targetToChain map[engine.Target][]*chain.Chain
type inmemoryLocalStorage struct {
- usedChainID map[string]struct{}
+ usedChainID map[chain.ID]struct{}
nameToResourceChains map[chain.Name]targetToChain
- guard *sync.RWMutex
}
func NewInmemoryLocalStorage() engine.LocalOverrideStorage {
return &inmemoryLocalStorage{
- usedChainID: map[string]struct{}{},
+ usedChainID: map[chain.ID]struct{}{},
nameToResourceChains: make(map[chain.Name]targetToChain),
- guard: &sync.RWMutex{},
}
}
@@ -36,28 +32,20 @@ func (s *inmemoryLocalStorage) generateChainID(name chain.Name, target engine.Ta
sid = strings.ReplaceAll(sid, "*", "")
sid = strings.ReplaceAll(sid, "/", ":")
sid = strings.ReplaceAll(sid, "::", ":")
- _, ok := s.usedChainID[sid]
+ id = chain.ID(sid)
+ _, ok := s.usedChainID[id]
if ok {
continue
}
- s.usedChainID[sid] = struct{}{}
-
- id = chain.ID(sid)
+ s.usedChainID[id] = struct{}{}
break
}
return id
}
func (s *inmemoryLocalStorage) AddOverride(name chain.Name, target engine.Target, c *chain.Chain) (chain.ID, error) {
- s.guard.Lock()
- defer s.guard.Unlock()
-
- if target.Name == "" {
- target.Name = "root"
- }
-
// AddOverride assigns generated chain ID if it has not been assigned.
- if len(c.ID) == 0 {
+ if c.ID == "" {
c.ID = s.generateChainID(name, target)
}
if s.nameToResourceChains[name] == nil {
@@ -65,7 +53,7 @@ func (s *inmemoryLocalStorage) AddOverride(name chain.Name, target engine.Target
}
rc := s.nameToResourceChains[name]
for i := range rc[target] {
- if bytes.Equal(rc[target][i].ID, c.ID) {
+ if rc[target][i].ID == c.ID {
rc[target][i] = c
return c.ID, nil
}
@@ -75,21 +63,15 @@ func (s *inmemoryLocalStorage) AddOverride(name chain.Name, target engine.Target
}
func (s *inmemoryLocalStorage) GetOverride(name chain.Name, target engine.Target, chainID chain.ID) (*chain.Chain, error) {
- s.guard.RLock()
- defer s.guard.RUnlock()
-
if _, ok := s.nameToResourceChains[name]; !ok {
return nil, engine.ErrChainNameNotFound
}
- if target.Name == "" {
- target.Name = "root"
- }
chains, ok := s.nameToResourceChains[name][target]
if !ok {
return nil, engine.ErrResourceNotFound
}
for _, c := range chains {
- if bytes.Equal(c.ID, chainID) {
+ if c.ID == chainID {
return c, nil
}
}
@@ -97,21 +79,15 @@ func (s *inmemoryLocalStorage) GetOverride(name chain.Name, target engine.Target
}
func (s *inmemoryLocalStorage) RemoveOverride(name chain.Name, target engine.Target, chainID chain.ID) error {
- s.guard.Lock()
- defer s.guard.Unlock()
-
if _, ok := s.nameToResourceChains[name]; !ok {
return engine.ErrChainNameNotFound
}
- if target.Name == "" {
- target.Name = "root"
- }
chains, ok := s.nameToResourceChains[name][target]
if !ok {
return engine.ErrResourceNotFound
}
for i, c := range chains {
- if bytes.Equal(c.ID, chainID) {
+ if c.ID == chainID {
s.nameToResourceChains[name][target] = append(chains[:i], chains[i+1:]...)
return nil
}
@@ -119,35 +95,11 @@ func (s *inmemoryLocalStorage) RemoveOverride(name chain.Name, target engine.Tar
return engine.ErrChainNotFound
}
-func (s *inmemoryLocalStorage) RemoveOverridesByTarget(name chain.Name, target engine.Target) error {
- s.guard.Lock()
- defer s.guard.Unlock()
-
- if _, ok := s.nameToResourceChains[name]; !ok {
- return engine.ErrChainNameNotFound
- }
- if target.Name == "" {
- target.Name = "root"
- }
- _, ok := s.nameToResourceChains[name][target]
- if ok {
- delete(s.nameToResourceChains[name], target)
- return nil
- }
- return engine.ErrResourceNotFound
-}
-
func (s *inmemoryLocalStorage) ListOverrides(name chain.Name, target engine.Target) ([]*chain.Chain, error) {
- s.guard.RLock()
- defer s.guard.RUnlock()
-
rcs, ok := s.nameToResourceChains[name]
if !ok {
return []*chain.Chain{}, nil
}
- if target.Name == "" {
- target.Name = "root"
- }
for t, chains := range rcs {
if t.Type != target.Type {
continue
@@ -161,20 +113,6 @@ func (s *inmemoryLocalStorage) ListOverrides(name chain.Name, target engine.Targ
}
func (s *inmemoryLocalStorage) DropAllOverrides(name chain.Name) error {
- s.guard.Lock()
- defer s.guard.Unlock()
-
s.nameToResourceChains[name] = make(targetToChain)
return nil
}
-
-func (s *inmemoryLocalStorage) ListOverrideDefinedTargets(name chain.Name) ([]engine.Target, error) {
- s.guard.RLock()
- defer s.guard.RUnlock()
- ttc := s.nameToResourceChains[name]
- var keys []engine.Target
- for k := range ttc {
- keys = append(keys, k)
- }
- return keys, nil
-}
diff --git a/pkg/engine/inmemory/local_storage_test.go b/pkg/engine/inmemory/local_storage_test.go
index f21d94d..3609070 100644
--- a/pkg/engine/inmemory/local_storage_test.go
+++ b/pkg/engine/inmemory/local_storage_test.go
@@ -14,7 +14,9 @@ const (
nonExistChainId = "ingress:LxGyWyL"
)
-var resrc = engine.ContainerTarget(container)
+var (
+ resrc = engine.ContainerTarget(container)
+)
func testInmemLocalStorage() *inmemoryLocalStorage {
return NewInmemoryLocalStorage().(*inmemoryLocalStorage)
@@ -110,52 +112,6 @@ func TestRemoveOverride(t *testing.T) {
require.True(t, ok)
require.Len(t, resourceChains, 0)
})
-
- t.Run("remove by target", func(t *testing.T) {
- inmem := testInmemLocalStorage()
- t0 := engine.ContainerTarget("name0")
- t1 := engine.ContainerTarget("name1")
- inmem.AddOverride(chain.Ingress, t0, &chain.Chain{
- ID: chain.ID(chainID),
- Rules: []chain.Rule{
- {
- Status: chain.AccessDenied,
- Actions: chain.Actions{Names: []string{"native::object::delete"}},
- Resources: chain.Resources{Names: []string{"native::object::*"}},
- },
- },
- })
- inmem.AddOverride(chain.Ingress, t0, &chain.Chain{
- ID: chain.ID(chainID),
- Rules: []chain.Rule{
- {
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{"native::object::delete"}},
- Resources: chain.Resources{Names: []string{"native::object::*"}},
- },
- },
- })
- inmem.AddOverride(chain.Ingress, t1, &chain.Chain{
- ID: chain.ID(chainID),
- Rules: []chain.Rule{
- {
- Status: chain.Allow,
- Actions: chain.Actions{Names: []string{"native::object::delete"}},
- Resources: chain.Resources{Names: []string{"native::object::*"}},
- },
- },
- })
-
- err := inmem.RemoveOverridesByTarget(chain.Ingress, t0)
- require.NoError(t, err)
-
- ingressChains, ok := inmem.nameToResourceChains[chain.Ingress]
- require.True(t, ok)
- require.Len(t, ingressChains, 1)
- resourceChains, ok := ingressChains[t1]
- require.True(t, ok)
- require.Len(t, resourceChains, 1)
- })
}
func TestGetOverride(t *testing.T) {
@@ -230,9 +186,6 @@ func TestListOverrides(t *testing.T) {
inmem.AddOverride(chain.Ingress, resrc, addChain)
l, _ := inmem.ListOverrides(chain.Ingress, resrc)
require.Len(t, l, 1)
- targets, err := inmem.ListOverrideDefinedTargets(chain.Ingress)
- require.NoError(t, err)
- require.Equal(t, []engine.Target{resrc}, targets)
})
t.Run("list after drop", func(t *testing.T) {
@@ -257,12 +210,12 @@ func TestGenerateID(t *testing.T) {
}
func hasDuplicates(ids []chain.ID) bool {
- seen := make(map[string]bool)
+ seen := make(map[chain.ID]bool)
for _, id := range ids {
- if seen[string(id)] {
+ if seen[id] {
return true
}
- seen[string(id)] = true
+ seen[id] = true
}
return false
}
diff --git a/pkg/engine/inmemory/morph_storage.go b/pkg/engine/inmemory/morph_storage.go
index 4aa96e0..72b5439 100644
--- a/pkg/engine/inmemory/morph_storage.go
+++ b/pkg/engine/inmemory/morph_storage.go
@@ -3,26 +3,27 @@ package inmemory
import (
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
- "github.com/google/uuid"
- "github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/util"
- "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
type inmemoryMorphRuleChainStorage struct {
- storage engine.LocalOverrideStorage
+ nameToNamespaceChains engine.LocalOverrideStorage
+ nameToContainerChains engine.LocalOverrideStorage
}
func NewInmemoryMorphRuleChainStorage() engine.MorphRuleChainStorage {
return &inmemoryMorphRuleChainStorage{
- storage: NewInmemoryLocalStorage(),
+ nameToNamespaceChains: NewInmemoryLocalStorage(),
+ nameToContainerChains: NewInmemoryLocalStorage(),
}
}
func (s *inmemoryMorphRuleChainStorage) AddMorphRuleChain(name chain.Name, target engine.Target, c *chain.Chain) (_ util.Uint256, _ uint32, err error) {
switch target.Type {
- case engine.Namespace, engine.Container, engine.User, engine.Group:
- _, err = s.storage.AddOverride(name, target, c)
+ case engine.Namespace:
+ _, err = s.nameToNamespaceChains.AddOverride(name, target, c)
+ case engine.Container:
+ _, err = s.nameToContainerChains.AddOverride(name, target, c)
default:
err = engine.ErrUnknownTarget
}
@@ -31,18 +32,10 @@ func (s *inmemoryMorphRuleChainStorage) AddMorphRuleChain(name chain.Name, targe
func (s *inmemoryMorphRuleChainStorage) RemoveMorphRuleChain(name chain.Name, target engine.Target, chainID chain.ID) (_ util.Uint256, _ uint32, err error) {
switch target.Type {
- case engine.Namespace, engine.Container, engine.User, engine.Group:
- err = s.storage.RemoveOverride(name, target, chainID)
- default:
- err = engine.ErrUnknownTarget
- }
- return
-}
-
-func (s *inmemoryMorphRuleChainStorage) RemoveMorphRuleChainsByTarget(name chain.Name, target engine.Target) (_ util.Uint256, _ uint32, err error) {
- switch target.Type {
- case engine.Namespace, engine.Container, engine.User, engine.Group:
- err = s.storage.RemoveOverridesByTarget(name, target)
+ case engine.Namespace:
+ err = s.nameToNamespaceChains.RemoveOverride(name, target, chainID)
+ case engine.Container:
+ err = s.nameToContainerChains.RemoveOverride(name, target, chainID)
default:
err = engine.ErrUnknownTarget
}
@@ -51,62 +44,11 @@ func (s *inmemoryMorphRuleChainStorage) RemoveMorphRuleChainsByTarget(name chain
func (s *inmemoryMorphRuleChainStorage) ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) {
switch target.Type {
- case engine.Namespace, engine.Container, engine.User, engine.Group:
- return s.storage.ListOverrides(name, target)
+ case engine.Namespace:
+ return s.nameToNamespaceChains.ListOverrides(name, target)
+ case engine.Container:
+ return s.nameToContainerChains.ListOverrides(name, target)
default:
}
return nil, engine.ErrUnknownTarget
}
-
-func (s *inmemoryMorphRuleChainStorage) GetAdmin() (util.Uint160, error) {
- panic("not implemented")
-}
-
-func (s *inmemoryMorphRuleChainStorage) SetAdmin(_ util.Uint160) (util.Uint256, uint32, error) {
- panic("not implemented")
-}
-
-func (s *inmemoryMorphRuleChainStorage) ListTargetsIterator(targetType engine.TargetType) (uuid uuid.UUID, it result.Iterator, err error) {
- it.Values = make([]stackitem.Item, 0)
-
- switch targetType {
- case engine.Namespace:
- // Listing targets may look bizarre, because inmemory rule chain storage use inmemory local overrides where
- // targets are listed by chain names.
- var targets []engine.Target
- targets, err = s.storage.ListOverrideDefinedTargets(chain.Ingress)
- if err != nil {
- return
- }
- for _, t := range targets {
- it.Values = append(it.Values, stackitem.NewByteArray([]byte(t.Name)))
- }
-
- targets, err = s.storage.ListOverrideDefinedTargets(chain.S3)
- if err != nil {
- return
- }
- for _, t := range targets {
- it.Values = append(it.Values, stackitem.NewByteArray([]byte(t.Name)))
- }
- case engine.Container:
- var targets []engine.Target
- targets, err = s.storage.ListOverrideDefinedTargets(chain.Ingress)
- if err != nil {
- return
- }
- for _, t := range targets {
- it.Values = append(it.Values, stackitem.NewByteArray([]byte(t.Name)))
- }
-
- targets, err = s.storage.ListOverrideDefinedTargets(chain.S3)
- if err != nil {
- return
- }
- for _, t := range targets {
- it.Values = append(it.Values, stackitem.NewByteArray([]byte(t.Name)))
- }
- default:
- }
- return
-}
diff --git a/pkg/engine/interface.go b/pkg/engine/interface.go
index ab026b3..8d11466 100644
--- a/pkg/engine/interface.go
+++ b/pkg/engine/interface.go
@@ -3,8 +3,6 @@ package engine
import (
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource"
- "github.com/google/uuid"
- "github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/util"
)
@@ -23,13 +21,9 @@ type LocalOverrideStorage interface {
RemoveOverride(name chain.Name, target Target, chainID chain.ID) error
- RemoveOverridesByTarget(name chain.Name, target Target) error
-
ListOverrides(name chain.Name, target Target) ([]*chain.Chain, error)
DropAllOverrides(name chain.Name) error
-
- ListOverrideDefinedTargets(name chain.Name) ([]Target, error)
}
type TargetType rune
@@ -37,8 +31,6 @@ type TargetType rune
const (
Namespace TargetType = 'n'
Container TargetType = 'c'
- User TargetType = 'u'
- Group TargetType = 'g'
)
type Target struct {
@@ -50,8 +42,6 @@ type Target struct {
type RequestTarget struct {
Namespace *Target
Container *Target
- User *Target
- Groups []Target
}
func NewRequestTargetWithNamespace(namespace string) RequestTarget {
@@ -77,36 +67,12 @@ func NewRequestTarget(namespace, container string) RequestTarget {
}
}
-func NewRequestTargetExtended(namespace, container, user string, groups []string) RequestTarget {
- nt := NamespaceTarget(namespace)
- ct := ContainerTarget(container)
- u := UserTarget(user)
- rt := RequestTarget{
- Namespace: &nt,
- Container: &ct,
- User: &u,
- }
- if len(groups) != 0 {
- rt.Groups = make([]Target, len(groups))
- for i := range groups {
- rt.Groups[i] = GroupTarget(groups[i])
- }
- }
- return rt
-}
-
func (rt *RequestTarget) Targets() (targets []Target) {
- if rt.Namespace != nil {
- targets = append(targets, *rt.Namespace)
- }
if rt.Container != nil {
targets = append(targets, *rt.Container)
}
- if rt.User != nil {
- targets = append(targets, *rt.User)
- }
- if len(rt.Groups) != 0 {
- targets = append(targets, rt.Groups...)
+ if rt.Namespace != nil {
+ targets = append(targets, *rt.Namespace)
}
return
}
@@ -125,47 +91,17 @@ func ContainerTarget(container string) Target {
}
}
-func UserTarget(user string) Target {
- return Target{
- Type: User,
- Name: user,
- }
-}
-
-func GroupTarget(group string) Target {
- return Target{
- Type: Group,
- Name: group,
- }
-}
-
-// MorphRuleChainStorageReader is the interface that provides read-only methods to receive
-// data like chains, target or admin from a chain storage.
-type MorphRuleChainStorageReader interface {
- // ListMorphRuleChains just lists deserialized chains.
- ListMorphRuleChains(name chain.Name, target Target) ([]*chain.Chain, error)
-
- GetAdmin() (util.Uint160, error)
-
- // ListTargetsIterator provides an iterator to list targets for which rules are defined.
- ListTargetsIterator(targetType TargetType) (uuid.UUID, result.Iterator, error)
-}
-
-// MorphRuleChainStorage is the interface to read and manage data within a chain storage.
+// MorphRuleChainStorage is the interface to manage chains from the chain storage.
// Basically, this implies that the storage manages rules stored in policy contract.
type MorphRuleChainStorage interface {
- MorphRuleChainStorageReader
-
// AddMorphRuleChain adds a chain rule to the policy contract and returns transaction hash, VUB and error.
AddMorphRuleChain(name chain.Name, target Target, c *chain.Chain) (util.Uint256, uint32, error)
// RemoveMorphRuleChain removes a chain rule to the policy contract and returns transaction hash, VUB and error.
RemoveMorphRuleChain(name chain.Name, target Target, chainID chain.ID) (util.Uint256, uint32, error)
- // RemoveMorphRuleChainsByTarget removes all chains by target and returns transaction hash, VUB and error.
- RemoveMorphRuleChainsByTarget(name chain.Name, target Target) (util.Uint256, uint32, error)
-
- SetAdmin(addr util.Uint160) (util.Uint256, uint32, error)
+ // ListMorphRuleChains just lists deserialized chains.
+ ListMorphRuleChains(name chain.Name, target Target) ([]*chain.Chain, error)
}
// Engine is the interface that provides methods to check request permissions checking
diff --git a/pkg/marshal/marshal.go b/pkg/marshal/marshal.go
deleted file mode 100644
index e296de6..0000000
--- a/pkg/marshal/marshal.go
+++ /dev/null
@@ -1,267 +0,0 @@
-package marshal
-
-import (
- "encoding/binary"
- "fmt"
-)
-
-const (
- Version byte = 0 // increase if breaking change
-
- ByteSize int = 1
- UInt8Size int = ByteSize
- BoolSize int = ByteSize
-
- nilSlice int64 = -1
- nilSliceSize int = 1
-
- byteTrue uint8 = 1
- byteFalse uint8 = 0
-
- // maxSliceLen taken from https://github.com/neo-project/neo/blob/38218bbee5bbe8b33cd8f9453465a19381c9a547/src/Neo/IO/Helper.cs#L77
- maxSliceLen = 0x1000000
-)
-
-type MarshallerError struct {
- errMsg string
- offset int
-}
-
-func (e *MarshallerError) Error() string {
- if e == nil {
- return ""
- }
- if e.offset < 0 {
- return e.errMsg
- }
- return fmt.Sprintf("%s (offset: %d)", e.errMsg, e.offset)
-}
-
-func errBufTooSmall(t string, marshal bool, offset int) error {
- action := "unmarshal"
- if marshal {
- action = "marshal"
- }
- return &MarshallerError{
- errMsg: fmt.Sprintf("not enough bytes left to %s value of type '%s'", action, t),
- offset: offset,
- }
-}
-
-func VerifyMarshal(buf []byte, lastOffset int) error {
- if len(buf) != lastOffset {
- return &MarshallerError{
- errMsg: "actual data size differs from expected",
- offset: -1,
- }
- }
- return nil
-}
-
-func VerifyUnmarshal(buf []byte, lastOffset int) error {
- if len(buf) != lastOffset {
- return &MarshallerError{
- errMsg: "unmarshalled bytes left",
- }
- }
- return nil
-}
-
-func SliceSize[T any](slice []T, sizeOf func(T) int) int {
- if slice == nil {
- return nilSliceSize
- }
- s := Int64Size(int64(len(slice)))
- for _, v := range slice {
- s += sizeOf(v)
- }
- return s
-}
-
-func SliceMarshal[T any](buf []byte, offset int, slice []T, marshalT func([]byte, int, T) (int, error)) (int, error) {
- if slice == nil {
- return Int64Marshal(buf, offset, nilSlice)
- }
- if len(slice) > maxSliceLen {
- return 0, &MarshallerError{
- errMsg: fmt.Sprintf("slice size if too big: '%d'", len(slice)),
- offset: offset,
- }
- }
- offset, err := Int64Marshal(buf, offset, int64(len(slice)))
- if err != nil {
- return 0, err
- }
- for _, v := range slice {
- offset, err = marshalT(buf, offset, v)
- if err != nil {
- return 0, err
- }
- }
- return offset, nil
-}
-
-func SliceUnmarshal[T any](buf []byte, offset int, unmarshalT func(buf []byte, offset int) (T, int, error)) ([]T, int, error) {
- size, offset, err := Int64Unmarshal(buf, offset)
- if err != nil {
- return nil, 0, err
- }
- if size == nilSlice {
- return nil, offset, nil
- }
- if size > maxSliceLen {
- return nil, 0, &MarshallerError{
- errMsg: fmt.Sprintf("slice size if too big: '%d'", size),
- offset: offset,
- }
- }
- if size < 0 {
- return nil, 0, &MarshallerError{
- errMsg: fmt.Sprintf("invalid slice size: '%d'", size),
- offset: offset,
- }
- }
- result := make([]T, size)
- for idx := 0; idx < len(result); idx++ {
- result[idx], offset, err = unmarshalT(buf, offset)
- if err != nil {
- return nil, 0, err
- }
- }
- return result, offset, nil
-}
-
-func Int64Size(v int64) int {
- // https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=92;drc=dac9b9ddbd5160c5f4552410f5f8281bd5eed38c
- // and
- // https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=41;drc=dac9b9ddbd5160c5f4552410f5f8281bd5eed38c
- ux := uint64(v) << 1
- if v < 0 {
- ux = ^ux
- }
- s := 0
- for ux >= 0x80 {
- s++
- ux >>= 7
- }
- return s + 1
-}
-
-func Int64Marshal(buf []byte, offset int, v int64) (int, error) {
- if len(buf)-offset < Int64Size(v) {
- return 0, errBufTooSmall("int64", true, offset)
- }
- return offset + binary.PutVarint(buf[offset:], v), nil
-}
-
-func Int64Unmarshal(buf []byte, offset int) (int64, int, error) {
- v, read := binary.Varint(buf[offset:])
- if read == 0 {
- return 0, 0, errBufTooSmall("int64", false, offset)
- }
- if read < 0 {
- return 0, 0, &MarshallerError{
- errMsg: "int64 unmarshal overflow",
- offset: offset,
- }
- }
- return v, offset + read, nil
-}
-
-func StringSize(s string) int {
- return Int64Size(int64(len(s))) + len(s)
-}
-
-func StringMarshal(buf []byte, offset int, s string) (int, error) {
- if len(s) > maxSliceLen {
- return 0, &MarshallerError{
- errMsg: fmt.Sprintf("string is too long: '%d'", len(s)),
- offset: offset,
- }
- }
- if len(buf)-offset < Int64Size(int64(len(s)))+len(s) {
- return 0, errBufTooSmall("string", true, offset)
- }
-
- offset, err := Int64Marshal(buf, offset, int64(len(s)))
- if err != nil {
- return 0, err
- }
- if s == "" {
- return offset, nil
- }
- return offset + copy(buf[offset:], s), nil
-}
-
-func StringUnmarshal(buf []byte, offset int) (string, int, error) {
- size, offset, err := Int64Unmarshal(buf, offset)
- if err != nil {
- return "", 0, err
- }
- if size == 0 {
- return "", offset, nil
- }
- if size > maxSliceLen {
- return "", 0, &MarshallerError{
- errMsg: fmt.Sprintf("string is too long: '%d'", size),
- offset: offset,
- }
- }
- if size < 0 {
- return "", 0, &MarshallerError{
- errMsg: fmt.Sprintf("invalid string size: '%d'", size),
- offset: offset,
- }
- }
- if len(buf)-offset < int(size) {
- return "", 0, errBufTooSmall("string", false, offset)
- }
- return string(buf[offset : offset+int(size)]), offset + int(size), nil
-}
-
-func UInt8Marshal(buf []byte, offset int, value uint8) (int, error) {
- if len(buf)-offset < 1 {
- return 0, errBufTooSmall("uint8", true, offset)
- }
- buf[offset] = value
- return offset + 1, nil
-}
-
-func UInt8Unmarshal(buf []byte, offset int) (uint8, int, error) {
- if len(buf)-offset < 1 {
- return 0, 0, errBufTooSmall("uint8", false, offset)
- }
- return buf[offset], offset + 1, nil
-}
-
-func ByteMarshal(buf []byte, offset int, value byte) (int, error) {
- return UInt8Marshal(buf, offset, value)
-}
-
-func ByteUnmarshal(buf []byte, offset int) (byte, int, error) {
- return UInt8Unmarshal(buf, offset)
-}
-
-func BoolMarshal(buf []byte, offset int, value bool) (int, error) {
- if value {
- return UInt8Marshal(buf, offset, byteTrue)
- }
- return UInt8Marshal(buf, offset, byteFalse)
-}
-
-func BoolUnmarshal(buf []byte, offset int) (bool, int, error) {
- v, offset, err := UInt8Unmarshal(buf, offset)
- if err != nil {
- return false, 0, err
- }
- if v == byteTrue {
- return true, offset, nil
- }
- if v == byteFalse {
- return false, offset, nil
- }
- return false, 0, &MarshallerError{
- errMsg: fmt.Sprintf("invalid marshalled value for bool: %d", v),
- offset: offset - BoolSize,
- }
-}
diff --git a/pkg/marshal/marshal_test.go b/pkg/marshal/marshal_test.go
deleted file mode 100644
index 5d3babd..0000000
--- a/pkg/marshal/marshal_test.go
+++ /dev/null
@@ -1,313 +0,0 @@
-package marshal
-
-import (
- "encoding/binary"
- "math"
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestMarshalling(t *testing.T) {
- t.Parallel()
- t.Run("slice", func(t *testing.T) {
- t.Parallel()
- t.Run("nil slice", func(t *testing.T) {
- t.Parallel()
-
- var int64s []int64
- expectedSize := SliceSize(int64s, Int64Size)
- require.Equal(t, 1, expectedSize)
- buf := make([]byte, expectedSize)
- offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
- require.NoError(t, err)
- require.NoError(t, VerifyMarshal(buf, offset))
-
- result, offset, err := SliceUnmarshal(buf, 0, Int64Unmarshal)
- require.NoError(t, err)
- require.NoError(t, VerifyUnmarshal(buf, offset))
- require.Nil(t, result)
- })
-
- t.Run("empty slice", func(t *testing.T) {
- t.Parallel()
-
- int64s := make([]int64, 0)
- expectedSize := SliceSize(int64s, Int64Size)
- require.Equal(t, 1, expectedSize)
- buf := make([]byte, expectedSize)
- offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
- require.NoError(t, err)
- require.NoError(t, VerifyMarshal(buf, offset))
-
- result, offset, err := SliceUnmarshal(buf, 0, Int64Unmarshal)
- require.NoError(t, err)
- require.NoError(t, VerifyUnmarshal(buf, offset))
- require.NotNil(t, result)
- require.Len(t, result, 0)
- })
-
- t.Run("non empty slice", func(t *testing.T) {
- t.Parallel()
-
- int64s := make([]int64, 100)
- for i := range int64s {
- int64s[i] = int64(i)
- }
- expectedSize := SliceSize(int64s, Int64Size)
- buf := make([]byte, expectedSize)
- offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
- require.NoError(t, err)
- require.NoError(t, VerifyMarshal(buf, offset))
-
- result, offset, err := SliceUnmarshal(buf, 0, Int64Unmarshal)
- require.NoError(t, err)
- require.NoError(t, VerifyUnmarshal(buf, offset))
- require.Equal(t, int64s, result)
- })
-
- t.Run("corrupted slice size", func(t *testing.T) {
- t.Parallel()
-
- int64s := make([]int64, 100)
- for i := range int64s {
- int64s[i] = int64(i)
- }
- expectedSize := SliceSize(int64s, Int64Size)
- buf := make([]byte, expectedSize)
- offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
- require.NoError(t, err)
- require.NoError(t, VerifyMarshal(buf, offset))
-
- for i := 0; i < binary.MaxVarintLen64; i++ {
- buf[i] = 129
- }
-
- _, _, err = SliceUnmarshal(buf, 0, Int64Unmarshal)
- var mErr *MarshallerError
- require.ErrorAs(t, err, &mErr)
-
- for i := 0; i < binary.MaxVarintLen64; i++ {
- buf[i] = 127
- }
- _, _, err = SliceUnmarshal(buf, 0, Int64Unmarshal)
- require.ErrorAs(t, err, &mErr)
- })
-
- t.Run("corrupted slice item", func(t *testing.T) {
- t.Parallel()
-
- int64s := make([]int64, 100)
- for i := range int64s {
- int64s[i] = int64(i)
- }
- expectedSize := SliceSize(int64s, Int64Size)
- buf := make([]byte, expectedSize)
- offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
- require.NoError(t, err)
- require.NoError(t, VerifyMarshal(buf, offset))
-
- for i := 2; i < binary.MaxVarintLen64+2; i++ {
- buf[i] = 129
- }
-
- _, _, err = SliceUnmarshal(buf, 0, Int64Unmarshal)
- var mErr *MarshallerError
- require.ErrorAs(t, err, &mErr)
- })
-
- t.Run("small buffer", func(t *testing.T) {
- t.Parallel()
-
- int64s := make([]int64, 100)
- for i := range int64s {
- int64s[i] = int64(i)
- }
- buf := make([]byte, 1)
- _, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
- var mErr *MarshallerError
- require.ErrorAs(t, err, &mErr)
-
- buf = make([]byte, 10)
- _, err = SliceMarshal(buf, 0, int64s, Int64Marshal)
- require.ErrorAs(t, err, &mErr)
- })
- })
-
- t.Run("int64", func(t *testing.T) {
- t.Parallel()
-
- t.Run("success", func(t *testing.T) {
- t.Parallel()
-
- require.Equal(t, 1, Int64Size(0))
- require.Equal(t, binary.MaxVarintLen64, Int64Size(math.MaxInt64))
- require.Equal(t, binary.MaxVarintLen64, Int64Size(math.MinInt64))
-
- for _, v := range []int64{0, math.MinInt64, math.MaxInt64} {
- size := Int64Size(v)
- buf := make([]byte, size)
- offset, err := Int64Marshal(buf, 0, v)
- require.NoError(t, err)
- require.NoError(t, VerifyMarshal(buf, offset))
-
- uv, offset, err := Int64Unmarshal(buf, 0)
- require.NoError(t, err)
- require.NoError(t, VerifyUnmarshal(buf, offset))
- require.Equal(t, v, uv)
- }
- })
-
- t.Run("invalid buffer", func(t *testing.T) {
- t.Parallel()
-
- var mErr *MarshallerError
-
- _, err := Int64Marshal([]byte{}, 0, 100500)
- require.ErrorAs(t, err, &mErr)
-
- _, _, err = Int64Unmarshal(nil, 0)
- require.ErrorAs(t, err, &mErr)
- })
-
- t.Run("overflow", func(t *testing.T) {
- t.Parallel()
-
- var mErr *MarshallerError
-
- var v int64 = math.MaxInt64
- buf := make([]byte, Int64Size(v))
- _, err := Int64Marshal(buf, 0, v)
- require.NoError(t, err)
-
- buf[9] = 2
-
- _, _, err = Int64Unmarshal(buf, 0)
- require.ErrorAs(t, err, &mErr)
- })
- })
-
- t.Run("string", func(t *testing.T) {
- t.Parallel()
-
- t.Run("success", func(t *testing.T) {
- t.Parallel()
- for _, v := range []string{
- "", "arn:aws:iam::namespace:group/some_group", "$Object:homomorphicHash",
- "native:container/ns/9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J",
- } {
- size := StringSize(v)
- buf := make([]byte, size)
- offset, err := StringMarshal(buf, 0, v)
- require.NoError(t, err)
- require.NoError(t, VerifyMarshal(buf, offset))
-
- uv, offset, err := StringUnmarshal(buf, 0)
- require.NoError(t, err)
- require.NoError(t, VerifyUnmarshal(buf, offset))
- require.Equal(t, v, uv)
- }
- })
-
- t.Run("invalid buffer", func(t *testing.T) {
- t.Parallel()
-
- str := "avada kedavra"
-
- var mErr *MarshallerError
- _, err := StringMarshal(nil, 0, str)
- require.ErrorAs(t, err, &mErr)
-
- _, _, err = StringUnmarshal(nil, 0)
- require.ErrorAs(t, err, &mErr)
-
- buf := make([]byte, StringSize(str))
- offset, err := StringMarshal(buf, 0, str)
- require.NoError(t, err)
- require.NoError(t, VerifyMarshal(buf, offset))
- buf = buf[:len(buf)-1]
- _, _, err = StringUnmarshal(buf, 0)
- require.ErrorAs(t, err, &mErr)
- })
- })
-
- t.Run("uint8, byte", func(t *testing.T) {
- t.Parallel()
-
- for _, v := range []byte{0, 8, 16, 32, 64, 128, 255} {
- buf := make([]byte, ByteSize)
- offset, err := ByteMarshal(buf, 0, v)
- require.NoError(t, err)
- require.NoError(t, VerifyMarshal(buf, offset))
-
- ub, offset, err := ByteUnmarshal(buf, 0)
- require.NoError(t, err)
- require.NoError(t, VerifyUnmarshal(buf, offset))
- require.Equal(t, v, ub)
-
- buf = make([]byte, UInt8Size)
- offset, err = UInt8Marshal(buf, 0, v)
- require.NoError(t, err)
- require.NoError(t, VerifyMarshal(buf, offset))
-
- uu, offset, err := UInt8Unmarshal(buf, 0)
- require.NoError(t, err)
- require.NoError(t, VerifyUnmarshal(buf, offset))
- require.Equal(t, v, uu)
- }
- })
-
- t.Run("bool", func(t *testing.T) {
- t.Parallel()
-
- t.Run("success", func(t *testing.T) {
- t.Parallel()
- for _, v := range []bool{false, true} {
- buf := make([]byte, BoolSize)
- offset, err := BoolMarshal(buf, 0, v)
- require.NoError(t, err)
- require.NoError(t, VerifyMarshal(buf, offset))
-
- ub, offset, err := BoolUnmarshal(buf, 0)
- require.NoError(t, err)
- require.NoError(t, VerifyUnmarshal(buf, offset))
- require.Equal(t, v, ub)
- }
- })
-
- t.Run("invalid value", func(t *testing.T) {
- t.Parallel()
- buf := make([]byte, BoolSize)
- offset, err := BoolMarshal(buf, 0, true)
- require.NoError(t, err)
- require.NoError(t, VerifyMarshal(buf, offset))
-
- buf[0] = 2
-
- _, _, err = BoolUnmarshal(buf, 0)
- var mErr *MarshallerError
- require.ErrorAs(t, err, &mErr)
- })
-
- t.Run("invalid buffer", func(t *testing.T) {
- t.Parallel()
- var mErr *MarshallerError
-
- _, err := BoolMarshal(nil, 0, true)
- require.ErrorAs(t, err, &mErr)
-
- buf := append(make([]byte, BoolSize), 100)
- offset, err := BoolMarshal(buf, 0, true)
- require.NoError(t, err)
- require.ErrorAs(t, VerifyMarshal(buf, offset), &mErr)
-
- v, offset, err := BoolUnmarshal(buf, 0)
- require.NoError(t, err)
- require.True(t, v)
- require.ErrorAs(t, VerifyUnmarshal(buf, offset), &mErr)
-
- _, _, err = BoolUnmarshal(nil, 0)
- require.ErrorAs(t, err, &mErr)
- })
- })
-}
diff --git a/pkg/morph/policy/policy_contract_storage.go b/pkg/morph/policy/policy_contract_storage.go
index ed5a062..6e85339 100644
--- a/pkg/morph/policy/policy_contract_storage.go
+++ b/pkg/morph/policy/policy_contract_storage.go
@@ -6,15 +6,11 @@ import (
"math/big"
"strings"
- "git.frostfs.info/TrueCloudLab/frostfs-contract/commonclient"
"git.frostfs.info/TrueCloudLab/frostfs-contract/policy"
client "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/policy"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
- "github.com/google/uuid"
- "github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
- neoinvoker "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/wallet"
@@ -26,66 +22,29 @@ var (
ErrEngineTargetTypeUnsupported = errors.New("this target type is not supported yet")
)
-// ContractStorage is the interface to manage chain rules within Policy contract.
+// ContractStorage is the interface to manage chain rules within the policy contract.
type ContractStorage struct {
- hash util.Uint160
-
- actor ContractStorageActor
-
contractInterface *client.Contract
}
var _ engine.MorphRuleChainStorage = (*ContractStorage)(nil)
-// ContractStorageReader is the interface to read data from Policy contract.
-type ContractStorageReader struct {
- hash util.Uint160
-
- invoker ContractStorageInvoker
-
- contractReaderInterface *client.ContractReader
-}
-
-type ContractStorageActor interface {
- client.Actor
- GetRPCInvoker() neoinvoker.RPCInvoke
-}
-
-var _ engine.MorphRuleChainStorageReader = (*ContractStorageReader)(nil)
-
-func NewContractStorage(actor ContractStorageActor, contract util.Uint160) *ContractStorage {
+func NewContractStorage(actor client.Actor, contract util.Uint160) *ContractStorage {
return &ContractStorage{
- hash: contract,
- actor: actor,
contractInterface: client.New(actor, contract),
}
}
-type contractStorageActorImpl struct {
- client.Actor
- rpcActor actor.RPCActor
-}
-
-var _ ContractStorageActor = &contractStorageActorImpl{}
-
-func (c *contractStorageActorImpl) GetRPCInvoker() neoinvoker.RPCInvoke {
- return c.rpcActor
-}
-
-// NewContractStorageWithSimpleActor constructs core actor from `rpcActor`.
-//
-// Note: NewContractStorageWithSimpleActor is appropriate only for call-only-once cases (for example, in CLIs). Otherwise, it is unsafe,
-// because core actor may use invalidated `rpcActor` if some connection errors occurred.
func NewContractStorageWithSimpleActor(rpcActor actor.RPCActor, acc *wallet.Account, contract util.Uint160) (*ContractStorage, error) {
act, err := actor.NewSimple(rpcActor, acc)
if err != nil {
return nil, fmt.Errorf("failed to create simple actor: %w", err)
}
- return NewContractStorage(&contractStorageActorImpl{Actor: act, rpcActor: rpcActor}, contract), nil
+ return NewContractStorage(act, contract), nil
}
func (s *ContractStorage) AddMorphRuleChain(name chain.Name, target engine.Target, c *chain.Chain) (txHash util.Uint256, vub uint32, err error) {
- if len(c.ID) == 0 {
+ if c.ID == "" {
err = ErrEmptyChainID
return
}
@@ -102,7 +61,7 @@ func (s *ContractStorage) AddMorphRuleChain(name chain.Name, target engine.Targe
}
func (s *ContractStorage) RemoveMorphRuleChain(name chain.Name, target engine.Target, chainID chain.ID) (txHash util.Uint256, vub uint32, err error) {
- if len(chainID) == 0 {
+ if chainID == "" {
err = ErrEmptyChainID
return
}
@@ -118,38 +77,17 @@ func (s *ContractStorage) RemoveMorphRuleChain(name chain.Name, target engine.Ta
return
}
-func (s *ContractStorage) RemoveMorphRuleChainsByTarget(name chain.Name, target engine.Target) (txHash util.Uint256, vub uint32, err error) {
- var kind policy.Kind
- kind, err = policyKind(target.Type)
- if err != nil {
- return
- }
- fullName := prefixedChainName(name, nil)
-
- txHash, vub, err = s.contractInterface.RemoveChainsByPrefix(big.NewInt(int64(kind)), target.Name, fullName)
- return
-}
-
-func listChains(name chain.Name, target engine.Target, rpcInvoker neoinvoker.RPCInvoke, hash util.Uint160) ([]*chain.Chain, error) {
+func (s *ContractStorage) ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) {
kind, err := policyKind(target.Type)
if err != nil {
return nil, err
}
- const (
- method = "iteratorChainsByPrefix"
- batchSize = neoinvoker.DefaultIteratorResultItems
- )
-
- inv := neoinvoker.New(rpcInvoker, nil)
- params := []any{
- big.NewInt(int64(kind)), target.Name, []byte(name),
- }
-
- items, err := commonclient.ReadIteratorItems(inv, batchSize, hash, method, params...)
+ items, err := s.contractInterface.ListChainsByPrefix(big.NewInt(int64(kind)), target.Name, []byte(name))
if err != nil {
- return nil, fmt.Errorf("read items error: %w", err)
+ return nil, err
}
+
var chains []*chain.Chain
for _, item := range items {
serialized, err := bytesFromStackItem(item)
@@ -162,58 +100,10 @@ func listChains(name chain.Name, target engine.Target, rpcInvoker neoinvoker.RPC
}
chains = append(chains, c)
}
+
return chains, nil
}
-func (s *ContractStorage) ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) {
- return listChains(name, target, s.actor.GetRPCInvoker(), s.hash)
-}
-
-func (s *ContractStorage) ListTargetsIterator(targetType engine.TargetType) (uuid.UUID, result.Iterator, error) {
- kind, err := policyKind(targetType)
- if err != nil {
- return uuid.UUID{}, result.Iterator{}, err
- }
- return s.contractInterface.ListTargets(big.NewInt(int64(kind)))
-}
-
-func (s *ContractStorage) GetAdmin() (util.Uint160, error) {
- return s.contractInterface.GetAdmin()
-}
-
-func (s *ContractStorage) SetAdmin(addr util.Uint160) (util.Uint256, uint32, error) {
- return s.contractInterface.SetAdmin(addr)
-}
-
-type ContractStorageInvoker interface {
- client.Invoker
- GetRPCInvoker() neoinvoker.RPCInvoke
-}
-
-func NewContractStorageReader(inv ContractStorageInvoker, contract util.Uint160) *ContractStorageReader {
- return &ContractStorageReader{
- hash: contract,
- invoker: inv,
- contractReaderInterface: client.NewReader(inv, contract),
- }
-}
-
-func (s *ContractStorageReader) ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) {
- return listChains(name, target, s.invoker.GetRPCInvoker(), s.hash)
-}
-
-func (s *ContractStorageReader) GetAdmin() (util.Uint160, error) {
- return s.contractReaderInterface.GetAdmin()
-}
-
-func (s *ContractStorageReader) ListTargetsIterator(targetType engine.TargetType) (uuid.UUID, result.Iterator, error) {
- kind, err := policyKind(targetType)
- if err != nil {
- return uuid.UUID{}, result.Iterator{}, err
- }
- return s.contractReaderInterface.ListTargets(big.NewInt(int64(kind)))
-}
-
func bytesFromStackItem(param stackitem.Item) ([]byte, error) {
switch param.Type() {
case stackitem.BufferT, stackitem.ByteArrayT, stackitem.IntegerT:
@@ -233,16 +123,10 @@ func prefixedChainName(name chain.Name, chainID chain.ID) []byte {
}
func policyKind(typ engine.TargetType) (policy.Kind, error) {
- switch typ {
- case engine.Namespace:
+ if typ == engine.Namespace {
return policy.Namespace, nil
- case engine.Container:
+ } else if typ == engine.Container {
return policy.Container, nil
- case engine.User:
- return policy.Kind(engine.User), nil
- case engine.Group:
- return policy.Kind(engine.Group), nil
- default:
- return policy.Kind(0), ErrEngineTargetTypeUnsupported
}
+ return policy.Kind(0), ErrEngineTargetTypeUnsupported
}
diff --git a/schema/common/consts.go b/schema/common/consts.go
deleted file mode 100644
index 5077a32..0000000
--- a/schema/common/consts.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package common
-
-const (
- PropertyKeyFrostFSIDGroupID = "frostfsid:groupID"
-
- PropertyKeyFrostFSSourceIP = "frostfs:sourceIP"
-
- PropertyKeyFormatFrostFSIDUserClaim = "frostfsid:userClaim/%s"
-
- PropertyKeyFrostFSXHeader = "frostfs:xheader/%s"
-)
diff --git a/schema/native/consts.go b/schema/native/consts.go
index bf31002..6277d53 100644
--- a/schema/native/consts.go
+++ b/schema/native/consts.go
@@ -8,17 +8,6 @@ const (
MethodSearchObject = "SearchObject"
MethodRangeObject = "RangeObject"
MethodHashObject = "HashObject"
- MethodPatchObject = "PatchObject"
-
- MethodPutContainer = "PutContainer"
- MethodDeleteContainer = "DeleteContainer"
- MethodGetContainer = "GetContainer"
- MethodListContainers = "ListContainers"
- MethodSetContainerEACL = "SetContainerEACL"
- MethodGetContainerEACL = "GetContainerEACL"
-
- ObjectPrefix = "native:object"
- ContainerPrefix = "native:container"
ResourceFormatNamespaceObjects = "native:object/%s/*"
ResourceFormatNamespaceContainerObjects = "native:object/%s/%s/*"
@@ -38,27 +27,15 @@ const (
ResourceFormatAllContainers = "native:container/*"
- PropertyKeyActorPublicKey = "$Actor:publicKey"
- PropertyKeyActorRole = "$Actor:role"
-
- PropertyKeyObjectVersion = "$Object:version"
- PropertyKeyObjectID = "$Object:objectID"
- PropertyKeyObjectContainerID = "$Object:containerID"
- PropertyKeyObjectOwnerID = "$Object:ownerID"
- PropertyKeyObjectCreationEpoch = "$Object:creationEpoch"
- PropertyKeyObjectPayloadLength = "$Object:payloadLength"
- PropertyKeyObjectPayloadHash = "$Object:payloadHash"
- PropertyKeyObjectType = "$Object:objectType"
- PropertyKeyObjectHomomorphicHash = "$Object:homomorphicHash"
- PropertyKeyFormatObjectContainerAttribute = "$Object:containerAttribute/%s"
-
- PropertyKeyContainerOwnerID = "$Container:ownerID"
- PropertyKeyFormatContainerAttribute = "$Container:attribute/%s"
-
- ProperyKeyTreeID = "$Tree:ID"
-
- PropertyValueContainerRoleOwner = "owner"
- PropertyValueContainerRoleIR = "ir"
- PropertyValueContainerRoleContainer = "container"
- PropertyValueContainerRoleOthers = "others"
+ PropertyKeyActorPublicKey = "$Actor:publicKey"
+ PropertyKeyActorRole = "$Actor:role"
+ PropertyKeyObjectVersion = "$Object:version"
+ PropertyKeyObjectID = "$Object:objectID"
+ PropertyKeyObjectContainerID = "$Object:containerID"
+ PropertyKeyObjectOwnerID = "$Object:ownerID"
+ PropertyKeyObjectCreationEpoch = "$Object:creationEpoch"
+ PropertyKeyObjectPayloadLength = "$Object:payloadLength"
+ PropertyKeyObjectPayloadHash = "$Object:payloadHash"
+ PropertyKeyObjectType = "$Object:objectType"
+ PropertyKeyObjectHomomorphicHash = "$Object:homomorphicHash"
)
diff --git a/schema/native/util/validation.go b/schema/native/util/validation.go
deleted file mode 100644
index 5ffccb2..0000000
--- a/schema/native/util/validation.go
+++ /dev/null
@@ -1,45 +0,0 @@
-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
deleted file mode 100644
index 8114935..0000000
--- a/schema/native/util/validation_test.go
+++ /dev/null
@@ -1,97 +0,0 @@
-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)
- }
- })
- }
-}
diff --git a/schema/s3/consts.go b/schema/s3/consts.go
index d90b7e8..a8de81f 100644
--- a/schema/s3/consts.go
+++ b/schema/s3/consts.go
@@ -6,19 +6,4 @@ const (
PropertyKeyDelimiter = "s3:delimiter"
PropertyKeyPrefix = "s3:prefix"
PropertyKeyVersionID = "s3:VersionId"
- PropertyKeyMaxKeys = "s3:max-keys"
-
- PropertyKeyFormatResourceTag = "aws:ResourceTag/%s"
- PropertyKeyFormatRequestTag = "aws:RequestTag/%s"
-
- PropertyKeyAccessBoxAttrMFA = "AccessBox-Attribute/IAM-MFA"
- PropertyKeyFormatAccessBoxAttr = "AccessBox-Attribute/%s"
-
- ResourceFormatS3All = "arn:aws:s3:::*"
- ResourceFormatS3Bucket = "arn:aws:s3:::%s"
- ResourceFormatS3BucketObjects = "arn:aws:s3:::%s/*"
- ResourceFormatS3BucketObject = "arn:aws:s3:::%s/%s"
-
- ResourceFormatIAMNamespaceUser = "arn:aws:iam::%s:user/%s"
- ResourceFormatIAMNamespaceGroup = "arn:aws:iam::%s:group/%s"
)