diff --git a/tests/testflows/rbac/__init__.py b/tests/testflows/rbac/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/testflows/rbac/configs/clickhouse/common.xml b/tests/testflows/rbac/configs/clickhouse/common.xml
new file mode 100644
index 00000000000..df952b28c82
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse/common.xml
@@ -0,0 +1,6 @@
+
+ Europe/Moscow
+ 0.0.0.0
+ /var/lib/clickhouse/
+ /var/lib/clickhouse/tmp/
+
diff --git a/tests/testflows/rbac/configs/clickhouse/config.d/logs.xml b/tests/testflows/rbac/configs/clickhouse/config.d/logs.xml
new file mode 100644
index 00000000000..bdf1bbc11c1
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse/config.d/logs.xml
@@ -0,0 +1,17 @@
+
+ 3
+
+ trace
+ /var/log/clickhouse-server/log.log
+ /var/log/clickhouse-server/log.err.log
+ 1000M
+ 10
+ /var/log/clickhouse-server/stderr.log
+ /var/log/clickhouse-server/stdout.log
+
+
+ system
+
+ 500
+
+
diff --git a/tests/testflows/rbac/configs/clickhouse/config.d/ports.xml b/tests/testflows/rbac/configs/clickhouse/config.d/ports.xml
new file mode 100644
index 00000000000..fbc6cea74c0
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse/config.d/ports.xml
@@ -0,0 +1,5 @@
+
+
+ 8443
+ 9440
+
\ No newline at end of file
diff --git a/tests/testflows/rbac/configs/clickhouse/config.d/remote.xml b/tests/testflows/rbac/configs/clickhouse/config.d/remote.xml
new file mode 100644
index 00000000000..302417c5891
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse/config.d/remote.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+ true
+
+ clickhouse1
+ 9000
+
+
+ clickhouse2
+ 9000
+
+
+ clickhouse3
+ 9000
+
+
+
+
+
+ true
+
+ clickhouse1
+ 9440
+ 1
+
+
+ clickhouse2
+ 9440
+ 1
+
+
+ clickhouse3
+ 9440
+ 1
+
+
+
+
+
+
+ clickhouse1
+ 9000
+
+
+
+
+ clickhouse2
+ 9000
+
+
+
+
+ clickhouse3
+ 9000
+
+
+
+
+
+
+ clickhouse1
+ 9440
+ 1
+
+
+
+
+ clickhouse2
+ 9440
+ 1
+
+
+
+
+ clickhouse3
+ 9440
+ 1
+
+
+
+
+
diff --git a/tests/testflows/rbac/configs/clickhouse/config.d/ssl.xml b/tests/testflows/rbac/configs/clickhouse/config.d/ssl.xml
new file mode 100644
index 00000000000..ca65ffd5e04
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse/config.d/ssl.xml
@@ -0,0 +1,17 @@
+
+
+
+ /etc/clickhouse-server/ssl/server.crt
+ /etc/clickhouse-server/ssl/server.key
+ none
+ true
+
+
+ true
+ none
+
+ AcceptCertificateHandler
+
+
+
+
diff --git a/tests/testflows/rbac/configs/clickhouse/config.d/storage.xml b/tests/testflows/rbac/configs/clickhouse/config.d/storage.xml
new file mode 100644
index 00000000000..618fd6b6d24
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse/config.d/storage.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ 1024
+
+
+
+
+
+
+ default
+
+
+
+
+
+
+
diff --git a/tests/testflows/rbac/configs/clickhouse/config.d/zookeeper.xml b/tests/testflows/rbac/configs/clickhouse/config.d/zookeeper.xml
new file mode 100644
index 00000000000..96270e7b645
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse/config.d/zookeeper.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ zookeeper
+ 2181
+
+ 15000
+
+
diff --git a/tests/testflows/rbac/configs/clickhouse/config.xml b/tests/testflows/rbac/configs/clickhouse/config.xml
new file mode 100644
index 00000000000..65187edf806
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse/config.xml
@@ -0,0 +1,436 @@
+
+
+
+
+
+ trace
+ /var/log/clickhouse-server/clickhouse-server.log
+ /var/log/clickhouse-server/clickhouse-server.err.log
+ 1000M
+ 10
+
+
+
+ 8123
+ 9000
+
+
+
+
+
+
+
+
+ /etc/clickhouse-server/server.crt
+ /etc/clickhouse-server/server.key
+
+ /etc/clickhouse-server/dhparam.pem
+ none
+ true
+ true
+ sslv2,sslv3
+ true
+
+
+
+ true
+ true
+ sslv2,sslv3
+ true
+
+
+
+ RejectCertificateHandler
+
+
+
+
+
+
+
+
+ 9009
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 4096
+ 3
+
+
+ 100
+
+
+
+
+
+ 8589934592
+
+
+ 5368709120
+
+
+
+ /var/lib/clickhouse/
+
+
+ /var/lib/clickhouse/tmp/
+
+
+ /var/lib/clickhouse/user_files/
+
+
+ /var/lib/clickhouse/access/
+
+
+ users.xml
+
+
+ default
+
+
+
+
+
+ default
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+ localhost
+ 9000
+
+
+
+
+
+
+ localhost
+ 9000
+
+
+
+
+ localhost
+ 9000
+
+
+
+
+
+
+ localhost
+ 9440
+ 1
+
+
+
+
+
+
+ localhost
+ 9000
+
+
+
+
+ localhost
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3600
+
+
+
+ 3600
+
+
+ 60
+
+
+
+
+
+
+
+
+
+ system
+
+
+ toYYYYMM(event_date)
+
+ 7500
+
+
+
+
+ system
+
+
+ toYYYYMM(event_date)
+ 7500
+
+
+
+
+ system
+
+ toYYYYMM(event_date)
+ 7500
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ *_dictionary.xml
+
+
+
+
+
+
+
+
+
+ /clickhouse/task_queue/ddl
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ click_cost
+ any
+
+ 0
+ 3600
+
+
+ 86400
+ 60
+
+
+
+ max
+
+ 0
+ 60
+
+
+ 3600
+ 300
+
+
+ 86400
+ 3600
+
+
+
+
+
+ /var/lib/clickhouse/format_schemas/
+
+
+
+
diff --git a/tests/testflows/rbac/configs/clickhouse/ssl/dhparam.pem b/tests/testflows/rbac/configs/clickhouse/ssl/dhparam.pem
new file mode 100644
index 00000000000..2e6cee0798d
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse/ssl/dhparam.pem
@@ -0,0 +1,8 @@
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEAua92DDli13gJ+//ZXyGaggjIuidqB0crXfhUlsrBk9BV1hH3i7fR
+XGP9rUdk2ubnB3k2ejBStL5oBrkHm9SzUFSQHqfDjLZjKoUpOEmuDc4cHvX1XTR5
+Pr1vf5cd0yEncJWG5W4zyUB8k++SUdL2qaeslSs+f491HBLDYn/h8zCgRbBvxhxb
+9qeho1xcbnWeqkN6Kc9bgGozA16P9NLuuLttNnOblkH+lMBf42BSne/TWt3AlGZf
+slKmmZcySUhF8aKfJnLKbkBCFqOtFRh8zBA9a7g+BT/lSANATCDPaAk1YVih2EKb
+dpc3briTDbRsiqg2JKMI7+VdULY9bh3EawIBAg==
+-----END DH PARAMETERS-----
diff --git a/tests/testflows/rbac/configs/clickhouse/ssl/server.crt b/tests/testflows/rbac/configs/clickhouse/ssl/server.crt
new file mode 100644
index 00000000000..7ade2d96273
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse/ssl/server.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/TCCAeWgAwIBAgIJANjx1QSR77HBMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
+BAMMCWxvY2FsaG9zdDAgFw0xODA3MzAxODE2MDhaGA8yMjkyMDUxNDE4MTYwOFow
+FDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAs9uSo6lJG8o8pw0fbVGVu0tPOljSWcVSXH9uiJBwlZLQnhN4SFSFohfI
+4K8U1tBDTnxPLUo/V1K9yzoLiRDGMkwVj6+4+hE2udS2ePTQv5oaMeJ9wrs+5c9T
+4pOtlq3pLAdm04ZMB1nbrEysceVudHRkQbGHzHp6VG29Fw7Ga6YpqyHQihRmEkTU
+7UCYNA+Vk7aDPdMS/khweyTpXYZimaK9f0ECU3/VOeG3fH6Sp2X6FN4tUj/aFXEj
+sRmU5G2TlYiSIUMF2JPdhSihfk1hJVALrHPTU38SOL+GyyBRWdNcrIwVwbpvsvPg
+pryMSNxnpr0AK0dFhjwnupIv5hJIOQIDAQABo1AwTjAdBgNVHQ4EFgQUjPLb3uYC
+kcamyZHK4/EV8jAP0wQwHwYDVR0jBBgwFoAUjPLb3uYCkcamyZHK4/EV8jAP0wQw
+DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAM/ocuDvfPus/KpMVD51j
+4IdlU8R0vmnYLQ+ygzOAo7+hUWP5j0yvq4ILWNmQX6HNvUggCgFv9bjwDFhb/5Vr
+85ieWfTd9+LTjrOzTw4avdGwpX9G+6jJJSSq15tw5ElOIFb/qNA9O4dBiu8vn03C
+L/zRSXrARhSqTW5w/tZkUcSTT+M5h28+Lgn9ysx4Ff5vi44LJ1NnrbJbEAIYsAAD
++UA+4MBFKx1r6hHINULev8+lCfkpwIaeS8RL+op4fr6kQPxnULw8wT8gkuc8I4+L
+P9gg/xDHB44T3ADGZ5Ib6O0DJaNiToO6rnoaaxs0KkotbvDWvRoxEytSbXKoYjYp
+0g==
+-----END CERTIFICATE-----
diff --git a/tests/testflows/rbac/configs/clickhouse/ssl/server.key b/tests/testflows/rbac/configs/clickhouse/ssl/server.key
new file mode 100644
index 00000000000..f0fb61ac443
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse/ssl/server.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCz25KjqUkbyjyn
+DR9tUZW7S086WNJZxVJcf26IkHCVktCeE3hIVIWiF8jgrxTW0ENOfE8tSj9XUr3L
+OguJEMYyTBWPr7j6ETa51LZ49NC/mhox4n3Cuz7lz1Pik62WreksB2bThkwHWdus
+TKxx5W50dGRBsYfMenpUbb0XDsZrpimrIdCKFGYSRNTtQJg0D5WTtoM90xL+SHB7
+JOldhmKZor1/QQJTf9U54bd8fpKnZfoU3i1SP9oVcSOxGZTkbZOViJIhQwXYk92F
+KKF+TWElUAusc9NTfxI4v4bLIFFZ01ysjBXBum+y8+CmvIxI3GemvQArR0WGPCe6
+ki/mEkg5AgMBAAECggEATrbIBIxwDJOD2/BoUqWkDCY3dGevF8697vFuZKIiQ7PP
+TX9j4vPq0DfsmDjHvAPFkTHiTQXzlroFik3LAp+uvhCCVzImmHq0IrwvZ9xtB43f
+7Pkc5P6h1l3Ybo8HJ6zRIY3TuLtLxuPSuiOMTQSGRL0zq3SQ5DKuGwkz+kVjHXUN
+MR2TECFwMHKQ5VLrC+7PMpsJYyOMlDAWhRfUalxC55xOXTpaN8TxNnwQ8K2ISVY5
+212Jz/a4hn4LdwxSz3Tiu95PN072K87HLWx3EdT6vW4Ge5P/A3y+smIuNAlanMnu
+plHBRtpATLiTxZt/n6npyrfQVbYjSH7KWhB8hBHtaQKBgQDh9Cq1c/KtqDtE0Ccr
+/r9tZNTUwBE6VP+3OJeKdEdtsfuxjOCkS1oAjgBJiSDOiWPh1DdoDeVZjPKq6pIu
+Mq12OE3Doa8znfCXGbkSzEKOb2unKZMJxzrz99kXt40W5DtrqKPNb24CNqTiY8Aa
+CjtcX+3weat82VRXvph6U8ltMwKBgQDLxjiQQzNoY7qvg7CwJCjf9qq8jmLK766g
+1FHXopqS+dTxDLM8eJSRrpmxGWJvNeNc1uPhsKsKgotqAMdBUQTf7rSTbt4MyoH5
+bUcRLtr+0QTK9hDWMOOvleqNXha68vATkohWYfCueNsC60qD44o8RZAS6UNy3ENq
+cM1cxqe84wKBgQDKkHutWnooJtajlTxY27O/nZKT/HA1bDgniMuKaz4R4Gr1PIez
+on3YW3V0d0P7BP6PWRIm7bY79vkiMtLEKdiKUGWeyZdo3eHvhDb/3DCawtau8L2K
+GZsHVp2//mS1Lfz7Qh8/L/NedqCQ+L4iWiPnZ3THjjwn3CoZ05ucpvrAMwKBgB54
+nay039MUVq44Owub3KDg+dcIU62U+cAC/9oG7qZbxYPmKkc4oL7IJSNecGHA5SbU
+2268RFdl/gLz6tfRjbEOuOHzCjFPdvAdbysanpTMHLNc6FefJ+zxtgk9sJh0C4Jh
+vxFrw9nTKKzfEl12gQ1SOaEaUIO0fEBGbe8ZpauRAoGAMAlGV+2/K4ebvAJKOVTa
+dKAzQ+TD2SJmeR1HZmKDYddNqwtZlzg3v4ZhCk4eaUmGeC1Bdh8MDuB3QQvXz4Dr
+vOIP4UVaOr+uM+7TgAgVnP4/K6IeJGzUDhX93pmpWhODfdu/oojEKVcpCojmEmS1
+KCBtmIrQLqzMpnBpLNuSY+Q=
+-----END PRIVATE KEY-----
diff --git a/tests/testflows/rbac/configs/clickhouse/users.xml b/tests/testflows/rbac/configs/clickhouse/users.xml
new file mode 100644
index 00000000000..86b2cd9e1e3
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse/users.xml
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+ 10000000000
+
+
+ 0
+
+
+ random
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+ ::/0
+
+
+
+ default
+
+
+ default
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3600
+
+
+ 0
+ 0
+ 0
+ 0
+ 0
+
+
+
+
diff --git a/tests/testflows/rbac/configs/clickhouse1/config.d/macros.xml b/tests/testflows/rbac/configs/clickhouse1/config.d/macros.xml
new file mode 100644
index 00000000000..6cdcc1b440c
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse1/config.d/macros.xml
@@ -0,0 +1,8 @@
+
+
+
+ clickhouse1
+ 01
+ 01
+
+
diff --git a/tests/testflows/rbac/configs/clickhouse2/config.d/macros.xml b/tests/testflows/rbac/configs/clickhouse2/config.d/macros.xml
new file mode 100644
index 00000000000..a114a9ce4ab
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse2/config.d/macros.xml
@@ -0,0 +1,8 @@
+
+
+
+ clickhouse2
+ 01
+ 02
+
+
diff --git a/tests/testflows/rbac/configs/clickhouse3/config.d/macros.xml b/tests/testflows/rbac/configs/clickhouse3/config.d/macros.xml
new file mode 100644
index 00000000000..904a27b0172
--- /dev/null
+++ b/tests/testflows/rbac/configs/clickhouse3/config.d/macros.xml
@@ -0,0 +1,8 @@
+
+
+
+ clickhouse3
+ 01
+ 03
+
+
diff --git a/tests/testflows/rbac/docker-compose/clickhouse-service.yml b/tests/testflows/rbac/docker-compose/clickhouse-service.yml
new file mode 100644
index 00000000000..9787b37abbb
--- /dev/null
+++ b/tests/testflows/rbac/docker-compose/clickhouse-service.yml
@@ -0,0 +1,28 @@
+version: '2.3'
+
+services:
+ clickhouse:
+ image: yandex/clickhouse-integration-test
+ expose:
+ - "9000"
+ - "9009"
+ - "8123"
+ volumes:
+ - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d:/etc/clickhouse-server/config.d"
+ - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.d/:/etc/clickhouse-server/users.d"
+ - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/ssl:/etc/clickhouse-server/ssl"
+ - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml"
+ - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml"
+ - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse"
+ - "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge"
+ entrypoint: bash -c "clickhouse server --config-file=/etc/clickhouse-server/config.xml --log-file=/var/log/clickhouse-server/clickhouse-server.log --errorlog-file=/var/log/clickhouse-server/clickhouse-server.err.log"
+ healthcheck:
+ test: clickhouse client --query='select 1'
+ interval: 3s
+ timeout: 2s
+ retries: 40
+ start_period: 2s
+ cap_add:
+ - SYS_PTRACE
+ security_opt:
+ - label:disable
diff --git a/tests/testflows/rbac/docker-compose/docker-compose.yml b/tests/testflows/rbac/docker-compose/docker-compose.yml
new file mode 100644
index 00000000000..efd60180800
--- /dev/null
+++ b/tests/testflows/rbac/docker-compose/docker-compose.yml
@@ -0,0 +1,63 @@
+version: '2.3'
+
+services:
+ zookeeper:
+ extends:
+ file: zookeeper-service.yml
+ service: zookeeper
+
+ clickhouse1:
+ extends:
+ file: clickhouse-service.yml
+ service: clickhouse
+ hostname: clickhouse1
+ volumes:
+ - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/database/:/var/lib/clickhouse/"
+ - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/logs/:/var/log/clickhouse-server/"
+ - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/config.d:/etc/clickhouse-server/config.d"
+ - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/users.d:/etc/clickhouse-server/users.d"
+ depends_on:
+ zookeeper:
+ condition: service_healthy
+
+ clickhouse2:
+ extends:
+ file: clickhouse-service.yml
+ service: clickhouse
+ hostname: clickhouse2
+ volumes:
+ - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/database/:/var/lib/clickhouse/"
+ - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/logs/:/var/log/clickhouse-server/"
+ - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/config.d:/etc/clickhouse-server/config.d"
+ - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/users.d:/etc/clickhouse-server/users.d"
+ depends_on:
+ zookeeper:
+ condition: service_healthy
+
+ clickhouse3:
+ extends:
+ file: clickhouse-service.yml
+ service: clickhouse
+ hostname: clickhouse3
+ volumes:
+ - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/database/:/var/lib/clickhouse/"
+ - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/logs/:/var/log/clickhouse-server/"
+ - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/config.d:/etc/clickhouse-server/config.d"
+ - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/users.d:/etc/clickhouse-server/users.d"
+ depends_on:
+ zookeeper:
+ condition: service_healthy
+
+ # dummy service which does nothing, but allows to postpone
+ # 'docker-compose up -d' till all dependecies will go healthy
+ all_services_ready:
+ image: hello-world
+ depends_on:
+ clickhouse1:
+ condition: service_healthy
+ clickhouse2:
+ condition: service_healthy
+ clickhouse3:
+ condition: service_healthy
+ zookeeper:
+ condition: service_healthy
diff --git a/tests/testflows/rbac/docker-compose/zookeeper-service.yml b/tests/testflows/rbac/docker-compose/zookeeper-service.yml
new file mode 100644
index 00000000000..f3df33358be
--- /dev/null
+++ b/tests/testflows/rbac/docker-compose/zookeeper-service.yml
@@ -0,0 +1,18 @@
+version: '2.3'
+
+services:
+ zookeeper:
+ image: zookeeper:3.4.12
+ expose:
+ - "2181"
+ environment:
+ ZOO_TICK_TIME: 500
+ ZOO_MY_ID: 1
+ healthcheck:
+ test: echo stat | nc localhost 2181
+ interval: 3s
+ timeout: 2s
+ retries: 5
+ start_period: 2s
+ security_opt:
+ - label:disable
diff --git a/tests/testflows/rbac/regression.py b/tests/testflows/rbac/regression.py
new file mode 100755
index 00000000000..6a96183298c
--- /dev/null
+++ b/tests/testflows/rbac/regression.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+import sys
+from testflows.core import *
+
+append_path(sys.path, "..")
+
+from helpers.cluster import Cluster
+from helpers.argparser import argparser
+from rbac.requirements import *
+
+issue_12507 = "https://github.com/ClickHouse/ClickHouse/issues/12507"
+issue_12510 = "https://github.com/ClickHouse/ClickHouse/issues/12510"
+issue_12600 = "https://github.com/ClickHouse/ClickHouse/issues/12600"
+
+xfails = {
+ "syntax/show create quota/I show create quota current":
+ [(Fail, "https://github.com/ClickHouse/ClickHouse/issues/12495")],
+ "syntax/create role/I create role that already exists, throws exception":
+ [(Fail, issue_12510)],
+ "syntax/create user/I create user with if not exists, user does exist":
+ [(Fail, issue_12507)],
+ "syntax/create row policy/I create row policy if not exists, policy does exist":
+ [(Fail, issue_12507)],
+ "syntax/create quota/I create quota if not exists, quota does exist":
+ [(Fail, issue_12507)],
+ "syntax/create role/I create role if not exists, role does exist":
+ [(Fail, issue_12507)],
+ "syntax/create settings profile/I create settings profile if not exists, profile does exist":
+ [(Fail, issue_12507)],
+ "syntax/grant privilege/grant privileges/privilege='dictGet', on=('db0.table0', 'db0.*', '*.*', 'tb0', '*'), allow_introspection=False":
+ [(Fail, issue_12600)],
+ "syntax/grant privilege/grant privileges/privilege='CREATE', on=('db0.table0', 'db0.*', '*.*', 'tb0', '*'), allow_introspection=False":
+ [(Fail, issue_12600)],
+ "syntax/grant privilege/grant privileges/privilege='DROP', on=('db0.table0', 'db0.*', '*.*', 'tb0', '*'), allow_introspection=False":
+ [(Fail, issue_12600)],
+ "syntax/grant privilege/grant privileges/privilege='TRUNCATE', on=('db0.table0', 'db0.*', '*.*', 'tb0', '*'), allow_introspection=False":
+ [(Fail, issue_12600)],
+ "syntax/grant privilege/grant privileges/privilege='OPTIMIZE', on=('db0.table0', 'db0.*', '*.*', 'tb0', '*'), allow_introspection=False":
+ [(Fail, issue_12600)],
+ "syntax/grant privilege/grant privileges/privilege='SYSTEM', on=('db0.table0', 'db0.*', '*.*', 'tb0', '*'), allow_introspection=False":
+ [(Fail, issue_12600)],
+}
+
+@TestModule
+@ArgumentParser(argparser)
+@XFails(xfails)
+@Name("rbac")
+def regression(self, local, clickhouse_binary_path):
+ """RBAC regression.
+ """
+ nodes = {
+ "clickhouse":
+ ("clickhouse1", "clickhouse2", "clickhouse3")
+ }
+ with Cluster(local, clickhouse_binary_path, nodes=nodes) as cluster:
+ self.context.cluster = cluster
+
+ Feature(run=load("rbac.tests.syntax.feature", "feature"), flags=TE)
+
+if main():
+ regression()
diff --git a/tests/testflows/rbac/requirements/__init__.py b/tests/testflows/rbac/requirements/__init__.py
new file mode 100644
index 00000000000..02f7d430154
--- /dev/null
+++ b/tests/testflows/rbac/requirements/__init__.py
@@ -0,0 +1 @@
+from .requirements import *
diff --git a/tests/testflows/rbac/requirements/requirements.py b/tests/testflows/rbac/requirements/requirements.py
new file mode 100644
index 00000000000..052206c88cd
--- /dev/null
+++ b/tests/testflows/rbac/requirements/requirements.py
@@ -0,0 +1,6132 @@
+# These requirements were auto generated
+# from software requirements specification (SRS)
+# document by TestFlows v1.6.200629.1155222.
+# Do not edit by hand but re-generate instead
+# using 'tfs requirements generate' command.
+from testflows.core import Requirement
+
+RQ_SRS_006_RBAC = Requirement(
+ name='RQ.SRS-006.RBAC',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support role based access control.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Login = Requirement(
+ name='RQ.SRS-006.RBAC.Login',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL only allow access to the server for a given\n'
+ 'user only when correct username and password are used during\n'
+ 'the connection to the server.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Login_DefaultUser = Requirement(
+ name='RQ.SRS-006.RBAC.Login.DefaultUser',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL use the **default user** when no username and password\n'
+ 'are specified during the connection to the server.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User = Requirement(
+ name='RQ.SRS-006.RBAC.User',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support creation and manipulation of\n'
+ 'one or more **user** accounts to which roles, privileges,\n'
+ 'settings profile, quotas and row policies can be assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Roles = Requirement(
+ name='RQ.SRS-006.RBAC.User.Roles',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning one or more **roles**\n'
+ 'to a **user**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Privileges = Requirement(
+ name='RQ.SRS-006.RBAC.User.Privileges',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning one or more privileges to a **user**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Variables = Requirement(
+ name='RQ.SRS-006.RBAC.User.Variables',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning one or more variables to a **user**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Variables_Constraints = Requirement(
+ name='RQ.SRS-006.RBAC.User.Variables.Constraints',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning min, max and read-only constraints\n'
+ 'for the variables that can be set and read by the **user**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_SettingsProfile = Requirement(
+ name='RQ.SRS-006.RBAC.User.SettingsProfile',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning one or more **settings profiles**\n'
+ 'to a **user**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Quotas = Requirement(
+ name='RQ.SRS-006.RBAC.User.Quotas',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning one or more **quotas** to a **user**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_RowPolicies = Requirement(
+ name='RQ.SRS-006.RBAC.User.RowPolicies',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning one or more **row policies** to a **user**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_AccountLock = Requirement(
+ name='RQ.SRS-006.RBAC.User.AccountLock',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support locking and unlocking of **user** accounts.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_AccountLock_DenyAccess = Requirement(
+ name='RQ.SRS-006.RBAC.User.AccountLock.DenyAccess',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL deny access to the user whose account is locked.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_DefaultRole = Requirement(
+ name='RQ.SRS-006.RBAC.User.DefaultRole',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning a default role to a **user**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_RoleSelection = Requirement(
+ name='RQ.SRS-006.RBAC.User.RoleSelection',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support selection of one or more **roles** from the available roles\n'
+ 'that are assigned to a **user**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_ShowCreate = Requirement(
+ name='RQ.SRS-006.RBAC.User.ShowCreate',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing the command of how **user** account was created.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_ShowPrivileges = Requirement(
+ name='RQ.SRS-006.RBAC.User.ShowPrivileges',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support listing the privileges of the **user**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role = Requirement(
+ name='RQ.SRS-006.RBAC.Role',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClikHouse] SHALL support creation and manipulation of **roles**\n'
+ 'to which privileges, settings profile, quotas and row policies can be\n'
+ 'assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Privileges = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Privileges',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning one or more privileges to a **role**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Variables = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Variables',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning one or more variables to a **role**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_SettingsProfile = Requirement(
+ name='RQ.SRS-006.RBAC.Role.SettingsProfile',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning one or more **settings profiles**\n'
+ 'to a **role**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Quotas = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Quotas',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning one or more **quotas** to a **role**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_RowPolicies = Requirement(
+ name='RQ.SRS-006.RBAC.Role.RowPolicies',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning one or more **row policies** to a **role**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Privileges_Usage = Requirement(
+ name='RQ.SRS-006.RBAC.Privileges.Usage',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting or revoking **usage** privilege\n'
+ 'for a database or a specific table to one or more **users** or **roles**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Privileges_Select = Requirement(
+ name='RQ.SRS-006.RBAC.Privileges.Select',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting or revoking **select** privilege\n'
+ 'for a database or a specific table to one or more **users** or **roles**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Privileges_SelectColumns = Requirement(
+ name='RQ.SRS-006.RBAC.Privileges.SelectColumns',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting or revoking **select columns** privilege\n'
+ 'for a specific table to one or more **users** or **roles**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Privileges_Insert = Requirement(
+ name='RQ.SRS-006.RBAC.Privileges.Insert',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting or revoking **insert** privilege\n'
+ 'for a database or a specific table to one or more **users** or **roles**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Privileges_Delete = Requirement(
+ name='RQ.SRS-006.RBAC.Privileges.Delete',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting or revoking **delete** privilege\n'
+ 'for a database or a specific table to one or more **users** or **roles**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Privileges_Alter = Requirement(
+ name='RQ.SRS-006.RBAC.Privileges.Alter',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting or revoking **alter** privilege\n'
+ 'for a database or a specific table to one or more **users** or **roles**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Privileges_Create = Requirement(
+ name='RQ.SRS-006.RBAC.Privileges.Create',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting or revoking **create** privilege\n'
+ 'for a database or a specific table to one or more **users** or **roles**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Privileges_Drop = Requirement(
+ name='RQ.SRS-006.RBAC.Privileges.Drop',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting or revoking **drop** privilege\n'
+ 'for a database or a specific table to one or more **users** or **roles**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Privileges_All = Requirement(
+ name='RQ.SRS-006.RBAC.Privileges.All',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL include in the **all** privilege the same rights\n'
+ 'as provided by **usage**, **select**, **select columns**,\n'
+ '**insert**, **delete**, **alter**, **create**, and **drop** privileges.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Privileges_All_GrantRevoke = Requirement(
+ name='RQ.SRS-006.RBAC.Privileges.All.GrantRevoke',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting or revoking **all** privileges\n'
+ 'for a database or a specific table to one or more **users** or **roles**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Privileges_GrantOption = Requirement(
+ name='RQ.SRS-006.RBAC.Privileges.GrantOption',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting or revoking **grant option** privilege\n'
+ 'for a database or a specific table to one or more **users** or **roles**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Privileges_AdminOption = Requirement(
+ name='RQ.SRS-006.RBAC.Privileges.AdminOption',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting or revoking **admin option** privilege\n'
+ 'to one or more **users** or **roles**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RequiredPrivileges_Insert = Requirement(
+ name='RQ.SRS-006.RBAC.RequiredPrivileges.Insert',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL not allow any `INSERT INTO` statements\n'
+ 'to be executed unless the user has the **insert** privilege for the destination table\n'
+ 'either because of the explicit grant or through one of the roles assigned to the user.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RequiredPrivileges_Select = Requirement(
+ name='RQ.SRS-006.RBAC.RequiredPrivileges.Select',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL not allow any `SELECT` statements\n'
+ 'to be executed unless the user has the **select** or **select columns** privilege\n'
+ 'for the destination table either because of the explicit grant\n'
+ 'or through one of the roles assigned to the user.\n'
+ 'If the the user only has the **select columns**\n'
+ 'privilege then only the specified columns SHALL be available for reading.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RequiredPrivileges_Create = Requirement(
+ name='RQ.SRS-006.RBAC.RequiredPrivileges.Create',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL not allow any `CREATE` statements\n'
+ 'to be executed unless the user has the **create** privilege for the destination database\n'
+ 'either because of the explicit grant or through one of the roles assigned to the user.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RequiredPrivileges_Alter = Requirement(
+ name='RQ.SRS-006.RBAC.RequiredPrivileges.Alter',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL not allow any `ALTER` statements\n'
+ 'to be executed unless the user has the **alter** privilege for the destination table\n'
+ 'either because of the explicit grant or through one of the roles assigned to the user.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RequiredPrivileges_Drop = Requirement(
+ name='RQ.SRS-006.RBAC.RequiredPrivileges.Drop',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL not allow any `DROP` statements\n'
+ 'to be executed unless the user has the **drop** privilege for the destination database\n'
+ 'either because of the explicit grant or through one of the roles assigned to the user.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RequiredPrivileges_Drop_Table = Requirement(
+ name='RQ.SRS-006.RBAC.RequiredPrivileges.Drop.Table',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL not allow any `DROP TABLE` statements\n'
+ 'to be executed unless the user has the **drop** privilege for the destination database or the table\n'
+ 'either because of the explicit grant or through one of the roles assigned to the user.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RequiredPrivileges_GrantRevoke = Requirement(
+ name='RQ.SRS-006.RBAC.RequiredPrivileges.GrantRevoke',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL not allow any `GRANT` or `REVOKE` statements\n'
+ 'to be executed unless the user has the **grant option** privilege\n'
+ 'for the privilege of the destination table\n'
+ 'either because of the explicit grant or through one of the roles assigned to the user.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RequiredPrivileges_Use = Requirement(
+ name='RQ.SRS-006.RBAC.RequiredPrivileges.Use',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL not allow the `USE` statement to be executed\n'
+ 'unless the user has at least one of the privileges for the database\n'
+ 'or the table inside that database\n'
+ 'either because of the explicit grant or through one of the roles assigned to the user.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RequiredPrivileges_Admin = Requirement(
+ name='RQ.SRS-006.RBAC.RequiredPrivileges.Admin',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL not allow any of the following statements\n'
+ '\n'
+ '* `SYSTEM`\n'
+ '* `SHOW`\n'
+ '* `ATTACH`\n'
+ '* `CHECK TABLE`\n'
+ '* `DESCRIBE TABLE`\n'
+ '* `DETACH`\n'
+ '* `EXISTS`\n'
+ '* `KILL QUERY`\n'
+ '* `KILL MUTATION`\n'
+ '* `OPTIMIZE`\n'
+ '* `RENAME`\n'
+ '* `TRUNCATE`\n'
+ '\n'
+ 'to be executed unless the user has the **admin option** privilege\n'
+ 'through one of the roles with **admin option** privilege assigned to the user.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_PartialRevokes = Requirement(
+ name='RQ.SRS-006.RBAC.PartialRevokes',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support partial revoking of privileges granted\n'
+ 'to a **user** or a **role**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support creation and manipulation of **settings profiles**\n'
+ 'that can include value definition for one or more variables and can\n'
+ 'can be assigned to one or more **users** or **roles**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Constraints = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Constraints',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning min, max and read-only constraints\n'
+ 'for the variables specified in the **settings profile**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_ShowCreate = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.ShowCreate',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing the command of how **setting profile** was created.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quotas = Requirement(
+ name='RQ.SRS-006.RBAC.Quotas',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support creation and manipulation of **quotas**\n'
+ 'that can be used to limit resource usage by a **user** or a **role**\n'
+ 'over a period of time.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quotas_Keyed = Requirement(
+ name='RQ.SRS-006.RBAC.Quotas.Keyed',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support creating **quotas** that are keyed\n'
+ 'so that a quota is tracked separately for each key value.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quotas_Queries = Requirement(
+ name='RQ.SRS-006.RBAC.Quotas.Queries',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support setting **queries** quota to limit the total number of requests.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quotas_Errors = Requirement(
+ name='RQ.SRS-006.RBAC.Quotas.Errors',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support setting **errors** quota to limit the number of queries that threw an exception.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quotas_ResultRows = Requirement(
+ name='RQ.SRS-006.RBAC.Quotas.ResultRows',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support setting **result rows** quota to limit the\n'
+ 'the total number of rows given as the result.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quotas_ReadRows = Requirement(
+ name='RQ.SRS-006.RBAC.Quotas.ReadRows',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support setting **read rows** quota to limit the total\n'
+ 'number of source rows read from tables for running the query on all remote servers.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quotas_ResultBytes = Requirement(
+ name='RQ.SRS-006.RBAC.Quotas.ResultBytes',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support setting **result bytes** quota to limit the total number\n'
+ 'of bytes that can be returned as the result.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quotas_ReadBytes = Requirement(
+ name='RQ.SRS-006.RBAC.Quotas.ReadBytes',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support setting **read bytes** quota to limit the total number\n'
+ 'of source bytes read from tables for running the query on all remote servers.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quotas_ExecutionTime = Requirement(
+ name='RQ.SRS-006.RBAC.Quotas.ExecutionTime',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support setting **execution time** quota to limit the maximum\n'
+ 'query execution time.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quotas_ShowCreate = Requirement(
+ name='RQ.SRS-006.RBAC.Quotas.ShowCreate',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing the command of how **quota** was created.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support creation and manipulation of table **row policies**\n'
+ 'that can be used to limit access to the table contents for a **user** or a **role**\n'
+ 'using a specified **condition**.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Condition = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Condition',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support row policy **conditions** that can be any SQL\n'
+ 'expression that returns a boolean.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_ShowCreate = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.ShowCreate',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing the command of how **row policy** was created.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Use_DefaultRole = Requirement(
+ name='RQ.SRS-006.RBAC.User.Use.DefaultRole',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL by default use default role or roles assigned\n'
+ 'to the user if specified.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Use_AllRolesWhenNoDefaultRole = Requirement(
+ name='RQ.SRS-006.RBAC.User.Use.AllRolesWhenNoDefaultRole',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL by default use all the roles assigned to the user\n'
+ 'if no default role or roles are specified for the user.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support creating **user** accounts using `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_IfNotExists = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.IfNotExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `IF NOT EXISTS` clause in the `CREATE USER` statement\n'
+ 'to skip raising an exception if a user with the same **name** already exists.\n'
+ 'If the `IF NOT EXISTS` clause is not specified then an exception SHALL be\n'
+ 'raised if a user with the same **name** already exists.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Replace = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Replace',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `OR REPLACE` clause in the `CREATE USER` statement\n'
+ 'to replace existing user account if already exists.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Password_NoPassword = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Password.NoPassword',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying no password when creating\n'
+ 'user account using `IDENTIFIED WITH NO_PASSWORD` clause .\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Password_NoPassword_Login = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Password.NoPassword.Login',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL use no password for the user when connecting to the server\n'
+ 'when an account was created with `IDENTIFIED WITH NO_PASSWORD` clause.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Password_PlainText = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Password.PlainText',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying plaintext password when creating\n'
+ 'user account using `IDENTIFIED WITH PLAINTEXT_PASSWORD BY` clause.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Password_PlainText_Login = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Password.PlainText.Login',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL use the plaintext password passed by the user when connecting to the server\n'
+ 'when an account was created with `IDENTIFIED WITH PLAINTEXT_PASSWORD` clause\n'
+ 'and compare the password with the one used in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Password_Sha256Password = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Password.Sha256Password',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying the result of applying SHA256\n'
+ 'to some password when creating user account using `IDENTIFIED WITH SHA256_PASSWORD BY` or `IDENTIFIED BY`\n'
+ 'clause.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Password_Sha256Password_Login = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Password.Sha256Password.Login',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL calculate `SHA256` of the password passed by the user when connecting to the server\n'
+ "when an account was created with `IDENTIFIED WITH SHA256_PASSWORD` or with 'IDENTIFIED BY' clause\n"
+ 'and compare the calculated hash to the one used in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Password_Sha256Hash = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Password.Sha256Hash',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying the result of applying SHA256\n'
+ 'to some already calculated hash when creating user account using `IDENTIFIED WITH SHA256_HASH`\n'
+ 'clause.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Password_Sha256Hash_Login = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Password.Sha256Hash.Login',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL calculate `SHA256` of the already calculated hash passed by\n'
+ 'the user when connecting to the server\n'
+ 'when an account was created with `IDENTIFIED WITH SHA256_HASH` clause\n'
+ 'and compare the calculated hash to the one used in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Password_DoubleSha1Password = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Password.DoubleSha1Password',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying the result of applying SHA1 two times\n'
+ 'to a password when creating user account using `IDENTIFIED WITH DOUBLE_SHA1_PASSWORD`\n'
+ 'clause.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Password_DoubleSha1Password_Login = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Password.DoubleSha1Password.Login',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL calculate `SHA1` two times over the password passed by\n'
+ 'the user when connecting to the server\n'
+ 'when an account was created with `IDENTIFIED WITH DOUBLE_SHA1_PASSWORD` clause\n'
+ 'and compare the calculated value to the one used in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Password_DoubleSha1Hash = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Password.DoubleSha1Hash',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying the result of applying SHA1 two times\n'
+ 'to a hash when creating user account using `IDENTIFIED WITH DOUBLE_SHA1_HASH`\n'
+ 'clause.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Password_DoubleSha1Hash_Login = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Password.DoubleSha1Hash.Login',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL calculate `SHA1` two times over the hash passed by\n'
+ 'the user when connecting to the server\n'
+ 'when an account was created with `IDENTIFIED WITH DOUBLE_SHA1_HASH` clause\n'
+ 'and compare the calculated value to the one used in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Host_Name = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Host.Name',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying one or more hostnames from\n'
+ 'which user can access the server using the `HOST NAME` clause\n'
+ 'in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Host_Regexp = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Host.Regexp',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying one or more regular expressions\n'
+ 'to match hostnames from which user can access the server\n'
+ 'using the `HOST REGEXP` clause in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Host_IP = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Host.IP',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying one or more IP address or subnet from\n'
+ 'which user can access the server using the `HOST IP` clause in the\n'
+ '`CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Host_Any = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Host.Any',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying `HOST ANY` clause in the `CREATE USER` statement\n'
+ 'to indicate that user can access the server from any host.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Host_None = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Host.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support fobidding access from any host using `HOST NONE` clause in the\n'
+ '`CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Host_Local = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Host.Local',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support limiting user access to local only using `HOST LOCAL` clause in the\n'
+ '`CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Host_Like = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Host.Like',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying host using `LIKE` command syntax using the\n'
+ '`HOST LIKE` clause in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Host_Default = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Host.Default',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support user access to server from any host\n'
+ 'if no `HOST` clause is specified in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_DefaultRole = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.DefaultRole',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying one or more default roles\n'
+ 'using `DEFAULT ROLE` clause in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_DefaultRole_None = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.DefaultRole.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying no default roles\n'
+ 'using `DEFAULT ROLE NONE` clause in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_DefaultRole_All = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.DefaultRole.All',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying all roles to be used as default\n'
+ 'using `DEFAULT ROLE ALL` clause in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Settings = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Settings',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying settings and profile\n'
+ 'using `SETTINGS` clause in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_OnCluster = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.OnCluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying cluster on which the user\n'
+ 'will be created using `ON CLUSTER` clause in the `CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Create_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.User.Create.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for `CREATE USER` statement.\n'
+ '\n'
+ '```sql\n'
+ 'CREATE USER [IF NOT EXISTS | OR REPLACE] name [ON CLUSTER cluster_name]\n'
+ " [IDENTIFIED [WITH {NO_PASSWORD|PLAINTEXT_PASSWORD|SHA256_PASSWORD|SHA256_HASH|DOUBLE_SHA1_PASSWORD|DOUBLE_SHA1_HASH}] BY {'password'|'hash'}]\n"
+ " [HOST {LOCAL | NAME 'name' | NAME REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]\n"
+ ' [DEFAULT ROLE role [,...]]\n'
+ " [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]\n"
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering **user** accounts using `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_OrderOfEvaluation = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.OrderOfEvaluation',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support evaluating `ALTER USER` statement from left to right\n'
+ 'where things defined on the right override anything that was previously defined on\n'
+ 'the left.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_IfExists = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.IfExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `IF EXISTS` clause in the `ALTER USER` statement\n'
+ 'to skip raising an exception (producing a warning instead) if a user with the specified **name** does not exist. If the `IF EXISTS` clause is not specified then an exception SHALL be raised if a user with the **name** does not exist.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Cluster = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Cluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying the cluster the user is on\n'
+ 'when altering user account using `ON CLUSTER` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Rename = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Rename',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying a new name for the user when\n'
+ 'altering user account using `RENAME` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Password_PlainText = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Password.PlainText',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying plaintext password when altering\n'
+ 'user account using `IDENTIFIED WITH PLAINTEXT_PASSWORD BY` or\n'
+ 'using shorthand `IDENTIFIED BY` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Password_Sha256Password = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Password.Sha256Password',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying the result of applying SHA256\n'
+ 'to some password as identification when altering user account using\n'
+ '`IDENTIFIED WITH SHA256_PASSWORD` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Password_DoubleSha1Password = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Password.DoubleSha1Password',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying the result of applying Double SHA1\n'
+ 'to some password as identification when altering user account using\n'
+ '`IDENTIFIED WITH DOUBLE_SHA1_PASSWORD` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Host_AddDrop = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Host.AddDrop',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering user by adding and dropping access to hosts with the `ADD HOST` or the `DROP HOST`in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Host_Local = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Host.Local',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support limiting user access to local only using `HOST LOCAL` clause in the\n'
+ '`ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Host_Name = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Host.Name',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying one or more hostnames from\n'
+ 'which user can access the server using the `HOST NAME` clause\n'
+ 'in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Host_Regexp = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Host.Regexp',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying one or more regular expressions\n'
+ 'to match hostnames from which user can access the server\n'
+ 'using the `HOST REGEXP` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Host_IP = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Host.IP',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying one or more IP address or subnet from\n'
+ 'which user can access the server using the `HOST IP` clause in the\n'
+ '`ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Host_Like = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Host.Like',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying sone or more similar hosts using `LIKE` command syntax using the `HOST LIKE` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Host_Any = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Host.Any',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying `HOST ANY` clause in the `ALTER USER` statement\n'
+ 'to indicate that user can access the server from any host.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Host_None = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Host.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support fobidding access from any host using `HOST NONE` clause in the\n'
+ '`ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_DefaultRole = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.DefaultRole',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying one or more default roles\n'
+ 'using `DEFAULT ROLE` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_DefaultRole_All = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.DefaultRole.All',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying all roles to be used as default\n'
+ 'using `DEFAULT ROLE ALL` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_DefaultRole_AllExcept = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.DefaultRole.AllExcept',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying one or more roles which will not be used as default\n'
+ 'using `DEFAULT ROLE ALL EXCEPT` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Settings = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Settings',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying one or more variables\n'
+ 'using `SETTINGS` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Settings_Min = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Settings.Min',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying a minimum value for the variable specifed using `SETTINGS` with `MIN` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Settings_Max = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Settings.Max',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying a maximum value for the variable specifed using `SETTINGS` with `MAX` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Settings_Profile = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Settings.Profile',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying the name of a profile for the variable specifed using `SETTINGS` with `PROFILE` clause in the `ALTER USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Alter_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.User.Alter.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `ALTER USER` statement.\n'
+ '\n'
+ '```sql\n'
+ 'ALTER USER [IF EXISTS] name [ON CLUSTER cluster_name]\n'
+ ' [RENAME TO new_name]\n'
+ " [IDENTIFIED [WITH {PLAINTEXT_PASSWORD|SHA256_PASSWORD|DOUBLE_SHA1_PASSWORD}] BY {'password'|'hash'}]\n"
+ " [[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]\n"
+ ' [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]\n'
+ " [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]\n"
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SetDefaultRole = Requirement(
+ name='RQ.SRS-006.RBAC.SetDefaultRole',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support setting or changing granted roles to default for one or more\n'
+ 'users using `SET DEFAULT ROLE` statement which\n'
+ 'SHALL permanently change the default roles for the user or users if successful.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SetDefaultRole_CurrentUser = Requirement(
+ name='RQ.SRS-006.RBAC.SetDefaultRole.CurrentUser',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support setting or changing granted roles to default for\n'
+ 'the current user using `CURRENT_USER` clause in the `SET DEFAULT ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SetDefaultRole_All = Requirement(
+ name='RQ.SRS-006.RBAC.SetDefaultRole.All',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support setting or changing all granted roles to default\n'
+ 'for one or more users using `ALL` clause in the `SET DEFAULT ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SetDefaultRole_AllExcept = Requirement(
+ name='RQ.SRS-006.RBAC.SetDefaultRole.AllExcept',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support setting or changing all granted roles except those specified\n'
+ 'to default for one or more users using `ALL EXCEPT` clause in the `SET DEFAULT ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SetDefaultRole_None = Requirement(
+ name='RQ.SRS-006.RBAC.SetDefaultRole.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support removing all granted roles from default\n'
+ 'for one or more users using `NONE` clause in the `SET DEFAULT ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SetDefaultRole_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.SetDefaultRole.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `SET DEFAULT ROLE` statement.\n'
+ '\n'
+ '```sql\n'
+ 'SET DEFAULT ROLE \n'
+ ' {NONE | role [,...] | ALL | ALL EXCEPT role [,...]} \n'
+ ' TO {user|CURRENT_USER} [,...]\n'
+ '\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SetRole = Requirement(
+ name='RQ.SRS-006.RBAC.SetRole',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support activating role or roles for the current user\n'
+ 'using `SET ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SetRole_Default = Requirement(
+ name='RQ.SRS-006.RBAC.SetRole.Default',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support activating default roles for the current user\n'
+ 'using `DEFAULT` clause in the `SET ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SetRole_None = Requirement(
+ name='RQ.SRS-006.RBAC.SetRole.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support activating no roles for the current user\n'
+ 'using `NONE` clause in the `SET ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SetRole_All = Requirement(
+ name='RQ.SRS-006.RBAC.SetRole.All',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support activating all roles for the current user\n'
+ 'using `ALL` clause in the `SET ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SetRole_AllExcept = Requirement(
+ name='RQ.SRS-006.RBAC.SetRole.AllExcept',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support activating all roles except those specified\n'
+ 'for the current user using `ALL EXCEPT` clause in the `SET ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SetRole_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.SetRole.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '```sql\n'
+ 'SET ROLE {DEFAULT | NONE | role [,...] | ALL | ALL EXCEPT role [,...]}\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_ShowCreateUser = Requirement(
+ name='RQ.SRS-006.RBAC.User.ShowCreateUser',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing the `CREATE USER` statement used to create the current user object\n'
+ 'using the `SHOW CREATE USER` statement with `CURRENT_USER` or no argument.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_ShowCreateUser_For = Requirement(
+ name='RQ.SRS-006.RBAC.User.ShowCreateUser.For',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing the `CREATE USER` statement used to create the specified user object\n'
+ 'using the `FOR` clause in the `SHOW CREATE USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_ShowCreateUser_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.User.ShowCreateUser.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing the following syntax for `SHOW CREATE USER` statement.\n'
+ '\n'
+ '```sql\n'
+ 'SHOW CREATE USER [name | CURRENT_USER]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Drop = Requirement(
+ name='RQ.SRS-006.RBAC.User.Drop',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support removing a user account using `DROP USER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Drop_IfExists = Requirement(
+ name='RQ.SRS-006.RBAC.User.Drop.IfExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support using `IF EXISTS` clause in the `DROP USER` statement\n'
+ 'to skip raising an exception if the user account does not exist.\n'
+ 'If the `IF EXISTS` clause is not specified then an exception SHALL be\n'
+ 'raised if a user does not exist.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Drop_OnCluster = Requirement(
+ name='RQ.SRS-006.RBAC.User.Drop.OnCluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support using `ON CLUSTER` clause in the `DROP USER` statement \n'
+ 'to specify the name of the cluster the user should be dropped from.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_User_Drop_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.User.Drop.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for `DROP USER` statement\n'
+ '\n'
+ '```sql\n'
+ 'DROP USER [IF EXISTS] name [,...] [ON CLUSTER cluster_name]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Create = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Create',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support creating a **role** using `CREATE ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Create_IfNotExists = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Create.IfNotExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `IF NOT EXISTS` clause in the `CREATE ROLE` statement\n'
+ 'to raising an exception if a role with the same **name** already exists.\n'
+ 'If the `IF NOT EXISTS` clause is not specified then an exception SHALL be\n'
+ 'raised if a role with the same **name** already exists.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Create_Replace = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Create.Replace',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `OR REPLACE` clause in the `CREATE ROLE` statement\n'
+ 'to replace existing role if it already exists.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Create_Settings = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Create.Settings',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying settings and profile using `SETTINGS` \n'
+ 'clause in the `CREATE ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Create_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Create.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `CREATE ROLE` statement\n'
+ '\n'
+ '``` sql\n'
+ 'CREATE ROLE [IF NOT EXISTS | OR REPLACE] name\n'
+ " [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]\n"
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Create_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Create.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL make the role available to be linked with users, privileges, quotas and\n'
+ 'settings profiles after the successful execution of the `CREATE ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Alter = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Alter',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering one **role** using `ALTER ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Alter_IfExists = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Alter.IfExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering one **role** using `ALTER ROLE IF EXISTS` statement, where no exception \n'
+ 'will be thrown if the role does not exist.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Alter_Cluster = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Alter.Cluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering one **role** using `ALTER ROLE role ON CLUSTER` statement to specify the \n'
+ 'cluster location of the specified role.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Alter_Rename = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Alter.Rename',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering one **role** using `ALTER ROLE role RENAME TO` statement which renames the \n'
+ 'role to a specified new name. If the new name already exists, that an exception SHALL be raised unless the \n'
+ '`IF EXISTS` clause is specified, by which no exception will be raised and nothing will change. \n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Alter_Settings = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Alter.Settings',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering the settings of one **role** using `ALTER ROLE role SETTINGS` statement.\n'
+ 'Altering variable values, creating max and min values, specifying readonly or writable, and specifying the\n'
+ 'profiles for which this alter change shall be applied to, are all supported, using the following syntax.\n'
+ '\n'
+ '```sql\n'
+ "[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]\n"
+ '```\n'
+ '\n'
+ 'One or more variables and profiles may be specified as shown above.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Alter_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Alter.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL alter the abilities granted by the role\n'
+ 'from all the users to which the role was assigned after the successful execution\n'
+ 'of the `ALTER ROLE` statement. Operations in progress SHALL be allowed to complete as is, but any new operation that requires the privileges that not otherwise granted to the user SHALL fail.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Alter_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Alter.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '```sql\n'
+ 'ALTER ROLE [IF EXISTS] name [ON CLUSTER cluster_name]\n'
+ ' [RENAME TO new_name]\n'
+ " [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]\n"
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Drop = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Drop',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support removing one or more roles using `DROP ROLE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Drop_IfExists = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Drop.IfExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support using `IF EXISTS` clause in the `DROP ROLE` statement\n'
+ 'to skip raising an exception if the role does not exist.\n'
+ 'If the `IF EXISTS` clause is not specified then an exception SHALL be\n'
+ 'raised if a role does not exist.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Drop_Cluster = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Drop.Cluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support using `ON CLUSTER` clause in the `DROP ROLE` statement to specify the cluster from which to drop the specified role.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Drop_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Drop.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the abilities granted by the role\n'
+ 'from all the users to which the role was assigned after the successful execution\n'
+ 'of the `DROP ROLE` statement. Operations in progress SHALL be allowed to complete\n'
+ 'but any new operation that requires the privileges that not otherwise granted to\n'
+ 'the user SHALL fail.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_Drop_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Role.Drop.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `DROP ROLE` statement\n'
+ '\n'
+ '``` sql\n'
+ 'DROP ROLE [IF EXISTS] name [,...] [ON CLUSTER cluster_name]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_ShowCreate = Requirement(
+ name='RQ.SRS-006.RBAC.Role.ShowCreate',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support viewing the settings for a role upon creation with the `SHOW CREATE ROLE`\n'
+ 'statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Role_ShowCreate_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Role.ShowCreate.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `SHOW CREATE ROLE` command.\n'
+ '\n'
+ '```sql\n'
+ 'SHOW CREATE ROLE name\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_To = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.To',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting privileges to one or more users or roles using `TO` clause\n'
+ 'in the `GRANT PRIVILEGE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_ToCurrentUser = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.ToCurrentUser',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting privileges to current user using `TO CURRENT_USER` clause\n'
+ 'in the `GRANT PRIVILEGE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Select = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Select',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **select** privilege to one or more users or roles\n'
+ 'for a database or a table using the `GRANT SELECT` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Select_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Select.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **select** privilege to the specified users or roles\n'
+ 'after the successful execution of the `GRANT SELECT` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **select** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_SelectColumns = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.SelectColumns',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **select columns** privilege to one or more users or roles\n'
+ 'for a database or a table using the `GRANT SELECT(columns)` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_SelectColumns_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.SelectColumns.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **select columns** privilege to the specified users or roles\n'
+ 'after the successful execution of the `GRANT SELECT(columns)` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **select columns** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Insert = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Insert',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **insert** privilege to one or more users or roles\n'
+ 'for a database or a table using the `GRANT INSERT` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Insert_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Insert.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **insert** privilege to the specified users or roles\n'
+ 'after the successful execution of the `GRANT INSERT` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **insert** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Alter = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Alter',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **alter** privilege to one or more users or roles\n'
+ 'for a database or a table using the `GRANT ALTER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Alter_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Alter.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **alter** privilege to the specified users or roles\n'
+ 'after the successful execution of the `GRANT ALTER` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **alter** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Create = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Create',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **create** privilege to one or more users or roles\n'
+ 'for a database or a table using the `GRANT CREATE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Create_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Create.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **create** privilege to the specified users or roles\n'
+ 'after the successful execution of the `GRANT CREATE` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **create** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Drop = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Drop',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **drop** privilege to one or more users or roles\n'
+ 'for a database or a table using the `GRANT DROP` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Drop_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Drop.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **drop** privilege to the specified users or roles\n'
+ 'after the successful execution of the `GRANT DROP` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **drop** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Truncate = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Truncate',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **truncate** privilege to one or more users or roles\n'
+ 'for a database or a table using `GRANT TRUNCATE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Truncate_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Truncate.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **truncate** privilege to the specified users or roles\n'
+ 'after the successful execution of the `GRANT TRUNCATE` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **truncate** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Optimize = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Optimize',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **optimize** privilege to one or more users or roles\n'
+ 'for a database or a table using `GRANT OPTIMIZE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Optimize_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Optimize.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **optimize** privilege to the specified users or roles\n'
+ 'after the successful execution of the `GRANT OPTIMIZE` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **optimize** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Show = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Show',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **show** privilege to one or more users or roles\n'
+ 'for a database or a table using `GRANT SHOW` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Show_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Show.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **show** privilege to the specified users or roles\n'
+ 'after the successful execution of the `GRANT SHOW` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **show** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_KillQuery = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.KillQuery',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **kill query** privilege to one or more users or roles\n'
+ 'for a database or a table using `GRANT KILL QUERY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_KillQuery_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.KillQuery.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **kill query** privilege to the specified users or roles\n'
+ 'after the successful execution of the `GRANT KILL QUERY` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **kill query** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_AccessManagement = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.AccessManagement',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **access management** privileges to one or more users or roles\n'
+ 'for a database or a table using `GRANT ACCESS MANAGEMENT` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_AccessManagement_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.AccessManagement.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **access management** privileges to the specified users or roles\n'
+ 'after the successful execution of the `GRANT ACCESS MANAGEMENT` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **access management** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_System = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.System',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **system** privileges to one or more users or roles\n'
+ 'for a database or a table using `GRANT SYSTEM` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_System_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.System.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **system** privileges to the specified users or roles\n'
+ 'after the successful execution of the `GRANT SYSTEM` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **system** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Introspection = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Introspection',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **introspection** privileges to one or more users or roles\n'
+ 'for a database or a table using `GRANT INTROSPECTION` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Introspection_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Introspection.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **introspection** privileges to the specified users or roles\n'
+ 'after the successful execution of the `GRANT INTROSPECTION` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **introspection** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Sources = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Sources',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **sources** privileges to one or more users or roles\n'
+ 'for a database or a table using `GRANT SOURCES` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Sources_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Sources.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **sources** privileges to the specified users or roles\n'
+ 'after the successful execution of the `GRANT SOURCES` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **sources** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_DictGet = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.DictGet',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **dictGet** privilege to one or more users or roles\n'
+ 'for a database or a table using `GRANT dictGet` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_DictGet_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.DictGet.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **dictGet** privileges to the specified users or roles\n'
+ 'after the successful execution of the `GRANT dictGet` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires the **dictGet** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_None = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting no privileges to one or more users or roles\n'
+ 'for a database or a table using `GRANT NONE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_None_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.None.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add no privileges to the specified users or roles\n'
+ 'after the successful execution of the `GRANT NONE` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires no privileges SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_All = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.All',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **all** privileges to one or more users or roles\n'
+ 'for a database or a table using the `GRANT ALL` or `GRANT ALL PRIVILEGES` statements.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_All_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.All.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **all** privileges to the specified users or roles\n'
+ 'after the successful execution of the `GRANT ALL` or `GRANT ALL PRIVILEGES` statement.\n'
+ 'Any new operation by a user or a user that has the specified role\n'
+ 'which requires one or more privileges that are part of the **all**\n'
+ 'privileges SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_GrantOption = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.GrantOption',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting the **grant option** privilege to one or more users or roles\n'
+ 'for a database or a table using the `WITH GRANT OPTION` clause in the `GRANT` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_GrantOption_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.GrantOption.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **grant option** privilege to the specified users or roles\n'
+ 'after the successful execution of the `GRANT` statement with the `WITH GRANT OPTION` clause\n'
+ 'for the privilege that was specified in the statement.\n'
+ 'Any new `GRANT` statements executed by a user or a user that has the specified role\n'
+ 'which requires **grant option** for the privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_On = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.On',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the `ON` clause in the `GRANT` privilege statement\n'
+ 'which SHALL allow to specify one or more tables to which the privilege SHALL\n'
+ 'be granted using the following patterns\n'
+ '\n'
+ '* `*.*` any table in any database\n'
+ '* `database.*` any table in the specified database\n'
+ '* `database.table` specific table in the specified database\n'
+ '* `*` any table in the current database\n'
+ '* `table` specific table in the current database\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_OnCluster = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.OnCluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying cluster on which to grant privileges using the `ON CLUSTER`\n'
+ 'clause in the `GRANT PRIVILEGE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Privilege_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Privilege.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `GRANT` statement that\n'
+ 'grants explicit privileges to a user or a role.\n'
+ '\n'
+ '```sql\n'
+ 'GRANT [ON CLUSTER cluster_name]\n'
+ ' privilege {SELECT | SELECT(columns) | INSERT | ALTER | CREATE | DROP | TRUNCATE | OPTIMIZE | SHOW | KILL QUERY | ACCESS MANAGEMENT | SYSTEM | INTROSPECTION | SOURCES | dictGet | NONE |ALL \t[PRIVILEGES]} [, ...]\n'
+ ' ON {*.* | database.* | database.table | * | table}\n'
+ ' TO {user | role | CURRENT_USER} [,...]\n'
+ ' [WITH GRANT OPTION]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Cluster = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Cluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking privileges to one or more users or roles\n'
+ 'for a database or a table on some specific cluster using the `REVOKE ON CLUSTER cluster_name` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Cluster_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Cluster.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove some privilege from the specified users or roles\n'
+ 'on cluster **cluster_name** after the successful execution of the \n'
+ '`REVOKE ON CLUSTER cluster_name some_privilege` statement. Any new operation by a user or a user \n'
+ 'that had the specified role which requires that privilege on cluster **cluster_name** SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Any = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Any',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking ANY privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE some_privilege` statement.\n'
+ '**some_privilege** refers to any Clickhouse defined privilege, whose hierarchy includes\n'
+ 'SELECT, INSERT, ALTER, CREATE, DROP, TRUNCATE, OPTIMIZE, SHOW, KILL QUERY, ACCESS MANAGEMENT,\n'
+ 'SYSTEM, INTROSPECTION, SOURCES, dictGet and all of their sub-privileges.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Any_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Any.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **some_privilege** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE some_privilege` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the privilege **some_privilege** SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Select = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Select',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **select** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE SELECT` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Select_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Select.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **select** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE SELECT` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **select** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Insert = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Insert',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **insert** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE INSERT` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Insert_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Insert.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **insert** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE INSERT` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **insert** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Alter = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Alter',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **alter** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE ALTER` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Alter_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Alter.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **alter** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE ALTER` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **alter** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Create = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Create',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **create** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE CREATE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Create_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Create.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **create** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE CREATE` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **create** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Drop = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Drop',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **drop** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE DROP` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Drop_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Drop.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **drop** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE DROP` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **drop** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Truncate = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Truncate',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **truncate** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE TRUNCATE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Truncate_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Truncate.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **truncate** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE TRUNCATE` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **truncate** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Optimize = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Optimize',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **optimize** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE OPTIMIZE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Optimize_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Optimize.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **optimize** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE OPTMIZE` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **optimize** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Show = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Show',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **show** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE SHOW` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Show_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Show.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **show** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE SHOW` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **show** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_KillQuery = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.KillQuery',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **kill query** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE KILL QUERY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_KillQuery_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.KillQuery.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **kill query** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE KILL QUERY` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **kill query** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_AccessManagement = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.AccessManagement',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **access management** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE ACCESS MANAGEMENT` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_AccessManagement_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.AccessManagement.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **access management** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE ACCESS MANAGEMENT` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **access management** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_System = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.System',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **system** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE SYSTEM` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_System_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.System.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **system** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE SYSTEM` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **system** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Introspection = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Introspection',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **introspection** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE INTROSPECTION` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Introspection_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Introspection.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **introspection** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE INTROSPECTION` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **introspection** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Sources = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Sources',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **sources** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE SOURCES` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Sources_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Sources.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **sources** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE SOURCES` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **sources** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_DictGet = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.DictGet',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the **dictGet** privilege to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE dictGet` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_DictGet_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.DictGet.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **dictGet** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE dictGet` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the **dictGet** privilege SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_PrivelegeColumns = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.PrivelegeColumns',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking the privilege **some_privilege** to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE some_privilege(column)` statement for one column.\n'
+ 'Multiple columns will be supported with `REVOKE some_privilege(column1, column2...)` statement.\n'
+ 'The privileges will be revoked for only the specified columns.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_PrivelegeColumns_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.PrivelegeColumns.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the privilege **some_privilege** from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE some_privilege(column)` statement for the specified column.\n'
+ 'Removal of the privilege **some_privilege** over multiple columns SHALL happen after the successful\n'
+ 'execution of the `REVOKE some_privilege(column1, column2...)` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires the privilege **some_privilege** over specified SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Multiple = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Multiple',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking MULTIPLE **privileges** to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE privilege1, privilege2...` statement.\n'
+ '**privileges** refers to any set of Clickhouse defined privilege, whose hierarchy includes\n'
+ 'SELECT, INSERT, ALTER, CREATE, DROP, TRUNCATE, OPTIMIZE, SHOW, KILL QUERY, ACCESS MANAGEMENT,\n'
+ 'SYSTEM, INTROSPECTION, SOURCES, dictGet and all of their sub-privileges.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Multiple_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Multiple.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **privileges** from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE privilege1, privilege2...` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires any of the **privileges** SHALL fail if user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_All = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.All',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking **all** privileges to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE ALL` or `REVOKE ALL PRIVILEGES` statements.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_All_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.All.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove **all** privileges from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE ALL` or `REVOKE ALL PRIVILEGES` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires one or more privileges that are part of **all**\n'
+ 'privileges SHALL fail.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_None = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking **no** privileges to one or more users or roles\n'
+ 'for a database or a table using the `REVOKE NONE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_None_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.None.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove **no** privileges from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE NONE` statement.\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'shall have the same effect after this command as it did before this command.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_On = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.On',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the `ON` clause in the `REVOKE` privilege statement\n'
+ 'which SHALL allow to specify one or more tables to which the privilege SHALL\n'
+ 'be revoked using the following patterns\n'
+ '\n'
+ '* `db.table` specific table in the specified database\n'
+ '* `db.*` any table in the specified database\n'
+ '* `*.*` any table in any database\n'
+ '* `table` specific table in the current database\n'
+ '* `*` any table in the current database\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_On_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.On.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the specificed priviliges from the specified one or more tables\n'
+ 'indicated with the `ON` clause in the `REVOKE` privilege statement.\n'
+ 'The tables will be indicated using the following patterns\n'
+ '\n'
+ '* `db.table` specific table in the specified database\n'
+ '* `db.*` any table in the specified database\n'
+ '* `*.*` any table in any database\n'
+ '* `table` specific table in the current database\n'
+ '* `*` any table in the current database\n'
+ '\n'
+ 'Any new operation by a user or a user that had the specified role\n'
+ 'which requires one or more privileges on the revoked tables SHALL fail.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_From = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.From',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the `FROM` clause in the `REVOKE` privilege statement\n'
+ 'which SHALL allow to specify one or more users to which the privilege SHALL\n'
+ 'be revoked using the following patterns\n'
+ '\n'
+ '* `{user | CURRENT_USER} [,...]` some combination of users by name, which may include the current user\n'
+ '* `ALL` all users\n'
+ '* `ALL EXCEPT {user | CURRENT_USER} [,...]` the logical reverse of the first pattern\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_From_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.From.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove **priviliges** to any set of users specified in the `FROM` clause \n'
+ 'in the `REVOKE` privilege statement. The details of the removed **privileges** will be specified\n'
+ 'in the other clauses. Any new operation by one of the specified users whose **privileges** have been\n'
+ 'revoked SHALL fail. The patterns that expand the `FROM` clause are listed below\n'
+ '\n'
+ '* `{user | CURRENT_USER} [,...]` some combination of users by name, which may include the current user\n'
+ '* `ALL` all users\n'
+ '* `ALL EXCEPT {user | CURRENT_USER} [,...]` the logical reverse of the first pattern\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Privilege_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Privilege.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `REVOKE` statement that\n'
+ 'revokes explicit privileges of a user or a role.\n'
+ '\n'
+ '```sql\n'
+ 'REVOKE [ON CLUSTER cluster_name] privilege\n'
+ ' [(column_name [,...])] [,...]\n'
+ ' ON {db.table|db.*|*.*|table|*} \n'
+ ' FROM {user | CURRENT_USER} [,...] | ALL | ALL EXCEPT {user | CURRENT_USER} [,...]\n'
+ '```\n'
+ '\n'
+ '\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_PartialRevoke_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.PartialRevoke.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support partial revokes by using `partial_revokes` variable\n'
+ 'that can be set or unset using the following syntax.\n'
+ '\n'
+ 'To disable partial revokes the `partial_revokes` variable SHALL be set to `0`\n'
+ '\n'
+ '```sql\n'
+ 'SET partial_revokes = 0\n'
+ '```\n'
+ '\n'
+ 'To enable partial revokes the `partial revokes` variable SHALL be set to `1`\n'
+ '\n'
+ '```sql\n'
+ 'SET partial_revokes = 1\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_PartialRevoke_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.PartialRevoke.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ 'FIXME: Need to be defined.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Role = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Role',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting one or more roles to\n'
+ 'one or more users or roles using the `GRANT` role statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Role_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Role.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add all the privileges that are assigned to the role\n'
+ 'which is granted to the user or the role to which `GRANT` role statement is applied.\n'
+ 'Any new operation that requires the privileges included in the role\n'
+ 'SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Role_CurrentUser = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Role.CurrentUser',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting one or more roles to current user using \n'
+ '`TO CURRENT_USER` clause in the `GRANT` role statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Role_CurrentUser_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Role.CurrentUser.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add all the privileges that are assigned to the role\n'
+ 'which is granted to the current user via the `GRANT` statement. Any new operation that \n'
+ 'requires the privileges included in the role SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Role_AdminOption = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Role.AdminOption',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support granting `admin option` privilege\n'
+ 'to one or more users or roles using the `WITH ADMIN OPTION` clause\n'
+ 'in the `GRANT` role statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Role_AdminOption_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Role.AdminOption.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL add the **admin option** privilege to the specified users or roles\n'
+ 'after the successful execution of the `GRANT` role statement with the `WITH ADMIN OPTION` clause.\n'
+ 'Any new **system queries** statements executed by a user or a user that has the specified role\n'
+ 'which requires the **admin option** privilege SHALL succeed.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Role_OnCluster = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Role.OnCluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying cluster on which the user is to be granted one or more roles\n'
+ 'using `ON CLUSTER` clause in the `GRANT` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Grant_Role_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Grant.Role.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for `GRANT` role statement\n'
+ '\n'
+ '``` sql\n'
+ 'GRANT\n'
+ ' ON CLUSTER cluster_name\n'
+ ' role [, role ...]\n'
+ ' TO {user | role | CURRENT_USER} [,...]\n'
+ ' [WITH ADMIN OPTION]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Role = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Role',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking one or more roles from\n'
+ 'one or more users or roles using the `REVOKE` role statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Role_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Role.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove all the privileges that are assigned to the role\n'
+ 'that is being revoked from the user or the role to which the `REVOKE` role statement is applied.\n'
+ 'Any new operation, by the user or users that have the role which included the role being revoked,\n'
+ 'that requires the privileges included in the role SHALL fail if the user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Role_Keywords = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Role.Keywords',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking one or more roles from\n'
+ 'special groupings of one or more users or roles with the `ALL`, `ALL EXCEPT`,\n'
+ 'and `CURRENT_USER` keywords.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Role_Keywords_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Role.Keywords.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove all the privileges that are assigned to the role\n'
+ 'that is being revoked from the user or the role to which the `REVOKE` role statement with the specified keywords is applied.\n'
+ 'Any new operation, by the user or users that have the role which included the role being revoked,\n'
+ 'that requires the privileges included in the role SHALL fail if the user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Role_Cluster = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Role.Cluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking one or more roles from\n'
+ 'one or more users or roles from one or more clusters \n'
+ 'using the `REVOKE ON CLUSTER` role statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Role_Cluster_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Role.Cluster.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove all the privileges that are assigned to the role\n'
+ 'that is being revoked from the user or the role from the cluster(s) \n'
+ 'to which the `REVOKE ON CLUSTER` role statement is applied.\n'
+ 'Any new operation, by the user or users that have the role which included the role being revoked,\n'
+ 'that requires the privileges included in the role SHALL fail if the user does not have it otherwise.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_AdminOption = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.AdminOption',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support revoking `admin option` privilege\n'
+ 'in one or more users or roles using the `ADMIN OPTION FOR` clause\n'
+ 'in the `REVOKE` role statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_AdminOption_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.AdminOption.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove the **admin option** privilege from the specified users or roles\n'
+ 'after the successful execution of the `REVOKE` role statement with the `ADMIN OPTION FOR` clause.\n'
+ 'Any new **system queries** statements executed by a user or a user that has the specified role\n'
+ 'which requires the **admin option** privilege SHALL fail.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Revoke_Role_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Revoke.Role.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `REVOKE` role statement\n'
+ '\n'
+ '```sql\n'
+ 'REVOKE [ON CLUSTER cluster_name] [ADMIN OPTION FOR] \n'
+ ' role [,...]\n'
+ ' FROM {user | role | CURRENT_USER} [,...] | ALL | ALL EXCEPT {user_name | role_name | CURRENT_USER} [,...]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Show_Grants = Requirement(
+ name='RQ.SRS-006.RBAC.Show.Grants',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support listing all the privileges granted to current user and role\n'
+ 'using the `SHOW GRANTS` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Show_Grants_For = Requirement(
+ name='RQ.SRS-006.RBAC.Show.Grants.For',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support listing all the privileges granted to a user or a role\n'
+ 'using the `FOR` clause in the `SHOW GRANTS` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Show_Grants_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Show.Grants.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[Clickhouse] SHALL use the following syntax for the `SHOW GRANTS` statement\n'
+ '\n'
+ '``` sql\n'
+ 'SHOW GRANTS [FOR user_or_role]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support creating settings profile using the `CREATE SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL use new profile after the `CREATE SETTINGS PROFILE` statement\n'
+ 'is successfully executed for any new operations performed by all the users and roles to which\n'
+ 'the settings profile is assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_IfNotExists = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.IfNotExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `IF NOT EXISTS` clause in the `CREATE SETTINGS PROFILE` statement\n'
+ 'to skip raising an exception if a settings profile with the same **name** already exists.\n'
+ 'If `IF NOT EXISTS` clause is not specified then an exception SHALL be raised if\n'
+ 'a settings profile with the same **name** already exists.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_Replace = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.Replace',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `OR REPLACE` clause in the `CREATE SETTINGS PROFILE` statement\n'
+ 'to replace existing settings profile if it already exists.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_Variables = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.Variables',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning values and constraints to one or more\n'
+ 'variables in the `CREATE SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_Variables_Value = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.Variables.Value',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning variable value in the `CREATE SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_Variables_Value_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.Variables.Value.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL use new variable values after `CREATE SETTINGS PROFILE` statement is\n'
+ 'successfully executed for any new operations performed by all the users and roles to which\n'
+ 'the settings profile is assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_Variables_Constraints = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.Variables.Constraints',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support setting `MIN`, `MAX`, `READONLY`, and `WRITABLE`\n'
+ 'constraints for the variables in the `CREATE SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_Variables_Constraints_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.Variables.Constraints.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL use new variable constraints after `CREATE SETTINGS PROFILE` statement is\n'
+ 'successfully executed for any new operations performed by all the users and roles to which\n'
+ 'the settings profile is assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_Assignment = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.Assignment',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning settings profile to one or more users\n'
+ 'or roles in the `CREATE SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_Assignment_None = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.Assignment.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning settings profile to no users or roles using\n'
+ '`TO NONE` clause in the `CREATE SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_Assignment_All = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.Assignment.All',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning settings profile to all current users and roles\n'
+ 'using `TO ALL` clause in the `CREATE SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_Assignment_AllExcept = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.Assignment.AllExcept',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support excluding assignment to one or more users or roles using\n'
+ 'the `ALL EXCEPT` clause in the `CREATE SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_Inherit = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.Inherit',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support inheriting profile settings from indicated profile using\n'
+ 'the `INHERIT` clause in the `CREATE SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_OnCluster = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.OnCluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying what cluster to create settings profile on\n'
+ 'using `ON CLUSTER` clause in the `CREATE SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Create_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Create.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `CREATE SETTINGS PROFILE` statement.\n'
+ '\n'
+ '``` sql\n'
+ 'CREATE SETTINGS PROFILE [IF NOT EXISTS | OR REPLACE] name\n'
+ ' [ON CLUSTER cluster_name]\n'
+ " [SET varname [= value] [MIN min] [MAX max] [READONLY|WRITABLE] | [INHERIT 'profile_name'] [,...]]\n"
+ ' [TO {user_or_role [,...] | NONE | ALL | ALL EXCEPT user_or_role [,...]}]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering settings profile using the `ALTER STETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL use the updated settings profile after `ALTER SETTINGS PROFILE`\n'
+ 'is successfully executed for any new operations performed by all the users and roles to which\n'
+ 'the settings profile is assigned or SHALL raise an exception if the settings profile does not exist.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_IfExists = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.IfExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `IF EXISTS` clause in the `ALTER SETTINGS PROFILE` statement\n'
+ 'to not raise exception if a settings profile does not exist.\n'
+ 'If the `IF EXISTS` clause is not specified then an exception SHALL be\n'
+ 'raised if a settings profile does not exist.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Rename = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Rename',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support renaming settings profile using the `RANAME TO` clause\n'
+ 'in the `ALTER SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Variables',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering values and constraints of one or more\n'
+ 'variables in the `ALTER SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables_Value = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Variables.Value',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering value of the variable in the `ALTER SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables_Value_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Variables.Value.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL use the new value of the variable after `ALTER SETTINGS PROFILE`\n'
+ 'is successfully executed for any new operations performed by all the users and roles to which\n'
+ 'the settings profile is assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables_Constraints = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Variables.Constraints',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering `MIN`, `MAX`, `READONLY`, and `WRITABLE`\n'
+ 'constraints for the variables in the `ALTER SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables_Constraints_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Variables.Constraints.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL use new constraints after `ALTER SETTINGS PROFILE`\n'
+ 'is successfully executed for any new operations performed by all the users and roles to which\n'
+ 'the settings profile is assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Assignment',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support reassigning settings profile to one or more users\n'
+ 'or roles using the `TO` clause in the `ALTER SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Assignment.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL unset all the variables and constraints that were defined in the settings profile\n'
+ 'in all users and roles to which the settings profile was previously assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_None = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Assignment.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support reassigning settings profile to no users or roles using the\n'
+ '`TO NONE` clause in the `ALTER SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_All = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Assignment.All',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support reassigning settings profile to all current users and roles\n'
+ 'using the `TO ALL` clause in the `ALTER SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_AllExcept = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Assignment.AllExcept',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support excluding assignment to one or more users or roles using\n'
+ 'the `TO ALL EXCEPT` clause in the `ALTER SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_Inherit = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Assignment.Inherit',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering the settings profile by inheriting settings from \n'
+ 'specified profile using `INHERIT` clause in the `ALTER SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_OnCluster = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Assignment.OnCluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering the settings profile on a specified cluster using\n'
+ '`ON CLUSTER` clause in the `ALTER SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Alter_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Alter.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `ALTER SETTINGS PROFILE` statement.\n'
+ '\n'
+ '``` sql\n'
+ 'ALTER SETTINGS PROFILE [IF EXISTS] name\n'
+ ' [ON CLUSTER cluster_name]\n'
+ ' [RENAME TO new_name]\n'
+ " [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | INHERIT 'profile_name'] [,...]\n"
+ ' [TO {user_or_role [,...] | NONE | ALL | ALL EXCEPT user_or_role [,...]]}\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Drop = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Drop',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support removing one or more settings profiles using the `DROP SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Drop_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Drop.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL unset all the variables and constraints that were defined in the settings profile\n'
+ 'in all the users and roles to which the settings profile was assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Drop_IfExists = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Drop.IfExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support using `IF EXISTS` clause in the `DROP SETTINGS PROFILE` statement\n'
+ 'to skip raising an exception if the settings profile does not exist.\n'
+ 'If the `IF EXISTS` clause is not specified then an exception SHALL be\n'
+ 'raised if a settings profile does not exist.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Drop_OnCluster = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Drop.OnCluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support dropping one or more settings profiles on specified cluster using\n'
+ '`ON CLUSTER` clause in the `DROP SETTINGS PROFILE` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_Drop_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.Drop.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `DROP SETTINGS PROFILE` statement\n'
+ '\n'
+ '``` sql\n'
+ 'DROP SETTINGS PROFILE [IF EXISTS] name [,name,...]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_SettingsProfile_ShowCreateSettingsProfile = Requirement(
+ name='RQ.SRS-006.RBAC.SettingsProfile.ShowCreateSettingsProfile',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing the `CREATE SETTINGS PROFILE` statement used to create the settings profile\n'
+ 'using the `SHOW CREATE SETTINGS PROFILE` statement with the following syntax\n'
+ '\n'
+ '``` sql\n'
+ 'SHOW CREATE SETTINGS PROFILE name\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support creating quotas using the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL use new limits specified by the quota after the `CREATE QUOTA` statement\n'
+ 'is successfully executed for any new operations performed by all the users and roles to which\n'
+ 'the quota is assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_IfNotExists = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.IfNotExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `IF NOT EXISTS` clause in the `CREATE QUOTA` statement\n'
+ 'to skip raising an exception if a quota with the same **name** already exists.\n'
+ 'If `IF NOT EXISTS` clause is not specified then an exception SHALL be raised if\n'
+ 'a quota with the same **name** already exists.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_Replace = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.Replace',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `OR REPLACE` clause in the `CREATE QUOTA` statement\n'
+ 'to replace existing quota if it already exists.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_Cluster = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.Cluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support creating quotas on a specific cluster with the \n'
+ '`ON CLUSTER` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_Interval = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.Interval',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support defining the quota interval that specifies\n'
+ 'a period of time over for which the quota SHALL apply using the\n'
+ '`FOR INTERVAL` clause in the `CREATE QUOTA` statement.\n'
+ '\n'
+ 'This statement SHALL also support a number and a time period which will be one \n'
+ 'of `{SECOND | MINUTE | HOUR | DAY | MONTH}`. Thus, the complete syntax SHALL be:\n'
+ '\n'
+ '`FOR INTERVAL number {SECOND | MINUTE | HOUR | DAY}` where number is some real number\n'
+ 'to define the interval.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_Interval_Randomized = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.Interval.Randomized',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support defining the quota randomized interval that specifies\n'
+ 'a period of time over for which the quota SHALL apply using the\n'
+ '`FOR RANDOMIZED INTERVAL` clause in the `CREATE QUOTA` statement.\n'
+ '\n'
+ 'This statement SHALL also support a number and a time period which will be one \n'
+ 'of `{SECOND | MINUTE | HOUR | DAY | MONTH}`. Thus, the complete syntax SHALL be:\n'
+ '\n'
+ '`FOR [RANDOMIZED] INTERVAL number {SECOND | MINUTE | HOUR | DAY}` where number is some \n'
+ 'real number to define the interval.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_Queries = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.Queries',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support limiting number of requests over a period of time\n'
+ 'using the `QUERIES` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_Errors = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.Errors',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support limiting number of queries that threw an exception\n'
+ 'using the `ERRORS` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_ResultRows = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.ResultRows',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support limiting the total number of rows given as the result\n'
+ 'using the `RESULT ROWS` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_ReadRows = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.ReadRows',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support limiting the total number of source rows read from tables\n'
+ 'for running the query on all remote servers\n'
+ 'using the `READ ROWS` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_ResultBytes = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.ResultBytes',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support limiting the total number of bytes that can be returned as the result\n'
+ 'using the `RESULT BYTES` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_ReadBytes = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.ReadBytes',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support limiting the total number of source bytes read from tables\n'
+ 'for running the query on all remote servers\n'
+ 'using the `READ BYTES` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_ExecutionTime = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.ExecutionTime',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support limiting the maximum query execution time\n'
+ 'using the `EXECUTION TIME` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_NoLimits = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.NoLimits',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support limiting the maximum query execution time\n'
+ 'using the `NO LIMITS` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_TrackingOnly = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.TrackingOnly',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support limiting the maximum query execution time\n'
+ 'using the `TRACKING ONLY` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_KeyedBy = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.KeyedBy',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support to track quota for some key\n'
+ 'following the `KEYED BY` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_KeyedByOptions = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.KeyedByOptions',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support to track quota separately for some parameter\n'
+ "using the `KEYED BY 'parameter'` clause in the `CREATE QUOTA` statement.\n"
+ '\n'
+ "'parameter' can be one of: \n"
+ "`{'none' | 'user name' | 'ip address' | 'client key' | 'client key or user name' | 'client key or ip address'}`\n"
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_Assignment = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.Assignment',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning quota to one or more users\n'
+ 'or roles using the `TO` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_Assignment_None = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.Assignment.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning quota to no users or roles using\n'
+ '`TO NONE` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_Assignment_All = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.Assignment.All',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning quota to all current users and roles\n'
+ 'using `TO ALL` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_Assignment_Except = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.Assignment.Except',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support excluding assignment of quota to one or more users or roles using\n'
+ 'the `EXCEPT` clause in the `CREATE QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Create_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Create.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `CREATE QUOTA` statement\n'
+ '\n'
+ '```sql\n'
+ 'CREATE QUOTA [IF NOT EXISTS | OR REPLACE] name [ON CLUSTER cluster_name]\n'
+ " [KEYED BY {'none' | 'user name' | 'ip address' | 'client key' | 'client key or user name' | 'client key or ip address'}]\n"
+ ' [FOR [RANDOMIZED] INTERVAL number {SECOND | MINUTE | HOUR | DAY}\n'
+ ' {MAX { {QUERIES | ERRORS | RESULT ROWS | RESULT BYTES | READ ROWS | READ BYTES | EXECUTION TIME} = number } [,...] |\n'
+ ' NO LIMITS | TRACKING ONLY} [,...]]\n'
+ ' [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering quotas using the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL use new limits specified by the updated quota after the `ALTER QUOTA` statement\n'
+ 'is successfully executed for any new operations performed by all the users and roles to which\n'
+ 'the quota is assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_IfExists = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.IfExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `IF EXISTS` clause in the `ALTER QUOTA` statement\n'
+ 'to skip raising an exception if a quota does not exist.\n'
+ 'If the `IF EXISTS` clause is not specified then an exception SHALL be raised if\n'
+ 'a quota does not exist.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_Rename = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.Rename',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `RENAME TO` clause in the `ALTER QUOTA` statement\n'
+ 'to rename the quota to the specified name.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_Cluster = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.Cluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering quotas on a specific cluster with the \n'
+ '`ON CLUSTER` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_Interval = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.Interval',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support redefining the quota interval that specifies\n'
+ 'a period of time over for which the quota SHALL apply using the\n'
+ '`FOR INTERVAL` clause in the `ALTER QUOTA` statement.\n'
+ '\n'
+ 'This statement SHALL also support a number and a time period which will be one \n'
+ 'of `{SECOND | MINUTE | HOUR | DAY | MONTH}`. Thus, the complete syntax SHALL be:\n'
+ '\n'
+ '`FOR INTERVAL number {SECOND | MINUTE | HOUR | DAY}` where number is some real number\n'
+ 'to define the interval.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_Interval_Randomized = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.Interval.Randomized',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support redefining the quota randomized interval that specifies\n'
+ 'a period of time over for which the quota SHALL apply using the\n'
+ '`FOR RANDOMIZED INTERVAL` clause in the `ALTER QUOTA` statement.\n'
+ '\n'
+ 'This statement SHALL also support a number and a time period which will be one \n'
+ 'of `{SECOND | MINUTE | HOUR | DAY | MONTH}`. Thus, the complete syntax SHALL be:\n'
+ '\n'
+ '`FOR [RANDOMIZED] INTERVAL number {SECOND | MINUTE | HOUR | DAY}` where number is some \n'
+ 'real number to define the interval.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_Queries = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.Queries',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering the limit of number of requests over a period of time\n'
+ 'using the `QUERIES` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_Errors = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.Errors',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering the limit of number of queries that threw an exception\n'
+ 'using the `ERRORS` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_ResultRows = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.ResultRows',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering the limit of the total number of rows given as the result\n'
+ 'using the `RESULT ROWS` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_ReadRows = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.ReadRows',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering the limit of the total number of source rows read from tables\n'
+ 'for running the query on all remote servers\n'
+ 'using the `READ ROWS` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_ALter_ResultBytes = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.ALter.ResultBytes',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering the limit of the total number of bytes that can be returned as the result\n'
+ 'using the `RESULT BYTES` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_ReadBytes = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.ReadBytes',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering the limit of the total number of source bytes read from tables\n'
+ 'for running the query on all remote servers\n'
+ 'using the `READ BYTES` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_ExecutionTime = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.ExecutionTime',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering the limit of the maximum query execution time\n'
+ 'using the `EXECUTION TIME` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_NoLimits = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.NoLimits',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support limiting the maximum query execution time\n'
+ 'using the `NO LIMITS` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_TrackingOnly = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.TrackingOnly',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support limiting the maximum query execution time\n'
+ 'using the `TRACKING ONLY` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_KeyedBy = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.KeyedBy',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering quota to track quota separately for some key\n'
+ 'following the `KEYED BY` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_KeyedByOptions = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.KeyedByOptions',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering quota to track quota separately for some parameter\n'
+ "using the `KEYED BY 'parameter'` clause in the `ALTER QUOTA` statement.\n"
+ '\n'
+ "'parameter' can be one of: \n"
+ "`{'none' | 'user name' | 'ip address' | 'client key' | 'client key or user name' | 'client key or ip address'}`\n"
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_Assignment = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.Assignment',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support reassigning quota to one or more users\n'
+ 'or roles using the `TO` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_Assignment_None = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.Assignment.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support reassigning quota to no users or roles using\n'
+ '`TO NONE` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_Assignment_All = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.Assignment.All',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support reassigning quota to all current users and roles\n'
+ 'using `TO ALL` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_Assignment_Except = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.Assignment.Except',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support excluding assignment of quota to one or more users or roles using\n'
+ 'the `EXCEPT` clause in the `ALTER QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Alter_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Alter.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `ALTER QUOTA` statement\n'
+ '\n'
+ '``` sql\n'
+ 'ALTER QUOTA [IF EXIST] name\n'
+ ' {{{QUERIES | ERRORS | RESULT ROWS | READ ROWS | RESULT BYTES | READ BYTES | EXECUTION TIME} number} [, ...] FOR INTERVAL number time_unit} [, ...]\n'
+ ' [KEYED BY USERNAME | KEYED BY IP | NOT KEYED] [ALLOW CUSTOM KEY | DISALLOW CUSTOM KEY]\n'
+ ' [TO {user_or_role [,...] | NONE | ALL} [EXCEPT user_or_role [,...]]]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Drop = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Drop',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support removing one or more quotas using the `DROP QUOTA` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Drop_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Drop.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL unset all the limits that were defined in the quota\n'
+ 'in all the users and roles to which the quota was assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Drop_IfExists = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Drop.IfExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support using `IF EXISTS` clause in the `DROP QUOTA` statement\n'
+ 'to skip raising an exception when the quota does not exist.\n'
+ 'If the `IF EXISTS` clause is not specified then an exception SHALL be\n'
+ 'raised if the quota does not exist.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Drop_Cluster = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Drop.Cluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support using `ON CLUSTER` clause in the `DROP QUOTA` statement\n'
+ 'to indicate the cluster the quota to be dropped is located on.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_Drop_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.Drop.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `DROP QUOTA` statement\n'
+ '\n'
+ '``` sql\n'
+ 'DROP QUOTA [IF EXISTS] name [,name...]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_ShowQuotas = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.ShowQuotas',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing all of the current quotas\n'
+ 'using the `SHOW QUOTAS` statement with the following syntax\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_ShowQuotas_IntoOutfile = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.ShowQuotas.IntoOutfile',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the `INTO OUTFILE` clause in the `SHOW QUOTAS` statement to define an outfile by some given string literal.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_ShowQuotas_Format = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.ShowQuotas.Format',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the `FORMAT` clause in the `SHOW QUOTAS` statement to define a format for the output quota list.\n'
+ '\n'
+ 'The types of valid formats are many, listed in output column:\n'
+ 'https://clickhouse.tech/docs/en/interfaces/formats/\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_ShowQuotas_Settings = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.ShowQuotas.Settings',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the `SETTINGS` clause in the `SHOW QUOTAS` statement to define settings in the showing of all quotas.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_ShowQuotas_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.ShowQuotas.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support using the `SHOW QUOTAS` statement \n'
+ 'with the following syntax\n'
+ '``` sql\n'
+ 'SHOW QUOTAS\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_ShowCreateQuota_Name = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.ShowCreateQuota.Name',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing the `CREATE QUOTA` statement used to create the quota with some given name\n'
+ 'using the `SHOW CREATE QUOTA` statement with the following syntax\n'
+ '\n'
+ '``` sql\n'
+ 'SHOW CREATE QUOTA name\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_ShowCreateQuota_Current = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.ShowCreateQuota.Current',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing the `CREATE QUOTA` statement used to create the CURRENT quota\n'
+ 'using the `SHOW CREATE QUOTA CURRENT` statement or the shorthand form\n'
+ '`SHOW CREATE QUOTA`\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_Quota_ShowCreateQuota_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.Quota.ShowCreateQuota.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax when\n'
+ 'using the `SHOW CREATE QUOTA` statement.\n'
+ '\n'
+ '```sql\n'
+ 'SHOW CREATE QUOTA [name | CURRENT]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support creating row policy using the `CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL use the new row policy to control access to the specified table\n'
+ 'after the `CREATE ROW POLICY` statement is successfully executed\n'
+ 'for any new operations on the table performed by all the users and roles to which\n'
+ 'the row policy is assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_IfNotExists = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.IfNotExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `IF NOT EXISTS` clause in the `CREATE ROW POLICY` statement\n'
+ 'to skip raising an exception if a row policy with the same **name** already exists.\n'
+ 'If the `IF NOT EXISTS` clause is not specified then an exception SHALL be raised if\n'
+ 'a row policy with the same **name** already exists.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_Replace = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.Replace',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support `OR REPLACE` clause in the `CREATE ROW POLICY` statement\n'
+ 'to replace existing row policy if it already exists.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_OnCluster = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.OnCluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying cluster on which to create the role policy\n'
+ 'using the `ON CLUSTER` clause in the `CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_On = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.On',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying table on which to create the role policy\n'
+ 'using the `ON` clause in the `CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_Access = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.Access',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support allowing or restricting access to rows using the\n'
+ '`AS` clause in the `CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_Access_Permissive = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.Access.Permissive',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support allowing access to rows using the\n'
+ '`AS PERMISSIVE` clause in the `CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_Access_Restrictive = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.Access.Restrictive',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support restricting access to rows using the\n'
+ '`AS RESTRICTIVE` clause in the `CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_ForSelect = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.ForSelect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying which rows are affected \n'
+ 'using the `FOR SELECT` clause in the `CREATE ROW POLICY` statement.\n'
+ 'REQUIRES CONFIRMATION\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_Condition = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.Condition',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying a condition that\n'
+ 'that can be any SQL expression which returns a boolean using the `USING`\n'
+ 'clause in the `CREATE ROW POLOCY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_Condition_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.Condition.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL check the condition specified in the row policy using the\n'
+ '`USING` clause in the `CREATE ROW POLICY` statement. The users or roles\n'
+ 'to which the row policy is assigned SHALL only see data for which\n'
+ 'the condition evaluates to the boolean value of `true`.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_Assignment = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.Assignment',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning row policy to one or more users\n'
+ 'or roles using the `TO` clause in the `CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_Assignment_None = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.Assignment.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning row policy to no users or roles using\n'
+ 'the `TO NONE` clause in the `CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_Assignment_All = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.Assignment.All',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support assigning row policy to all current users and roles\n'
+ 'using `TO ALL` clause in the `CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_Assignment_AllExcept = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.Assignment.AllExcept',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support excluding assignment of row policy to one or more users or roles using\n'
+ 'the `ALL EXCEPT` clause in the `CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Create_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Create.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `CRETE ROW POLICY` statement\n'
+ '\n'
+ '``` sql\n'
+ 'CREATE [ROW] POLICY [IF NOT EXISTS | OR REPLACE] policy_name [ON CLUSTER cluster_name] ON [db.]table\n'
+ ' [AS {PERMISSIVE | RESTRICTIVE}]\n'
+ ' [FOR SELECT]\n'
+ ' [USING condition]\n'
+ ' [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering row policy using the `ALTER ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL use the updated row policy to control access to the specified table\n'
+ 'after the `ALTER ROW POLICY` statement is successfully executed\n'
+ 'for any new operations on the table performed by all the users and roles to which\n'
+ 'the row policy is assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_IfExists = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.IfExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the `IF EXISTS` clause in the `ALTER ROW POLICY` statement\n'
+ 'to skip raising an exception if a row policy does not exist.\n'
+ 'If the `IF EXISTS` clause is not specified then an exception SHALL be raised if\n'
+ 'a row policy does not exist.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_ForSelect = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.ForSelect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support modifying rows on which to apply the row policy \n'
+ 'using the `FOR SELECT` clause in the `ALTER ROW POLICY` statement.\n'
+ 'REQUIRES FUNCTION CONFIRMATION.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_OnCluster = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.OnCluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying cluster on which to alter the row policy \n'
+ 'using the `ON CLUSTER` clause in the `ALTER ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_On = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.On',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support specifying table on which to alter the row policy \n'
+ 'using the `ON` clause in the `ALTER ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_Rename = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.Rename',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support renaming the row policy using the `RENAME` clause \n'
+ 'in the `ALTER ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_Access = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.Access',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support altering access to rows using the\n'
+ '`AS` clause in the `CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_Access_Permissive = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.Access.Permissive',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support permitting access to rows using the\n'
+ '`AS PERMISSIVE` clause in the `CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_Access_Restrictive = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.Access.Restrictive',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support restricting access to rows using the\n'
+ '`AS RESTRICTIVE` clause in the `CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_Condition = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.Condition',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support re-specifying the row policy condition\n'
+ 'using the `USING` clause in the `ALTER ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_Condition_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.Condition.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL check the new condition specified for the row policy using the\n'
+ '`USING` clause in the `ALTER ROW POLICY` statement. The users or roles\n'
+ 'to which the row policy is assigned SHALL only see data for which\n'
+ 'the new condition evaluates to the boolean value of `true`.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_Condition_None = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.Condition.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support removing the row policy condition\n'
+ 'using the `USING NONE` clause in the `ALTER ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_Assignment = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.Assignment',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support reassigning row policy to one or more users\n'
+ 'or roles using the `TO` clause in the `ALTER ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_Assignment_None = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.Assignment.None',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support reassigning row policy to no users or roles using\n'
+ 'the `TO NONE` clause in the `ALTER ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_Assignment_All = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.Assignment.All',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support reassigning row policy to all current users and roles\n'
+ 'using the `TO ALL` clause in the `ALTER ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_Assignment_AllExcept = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.Assignment.AllExcept',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support excluding assignment of row policy to one or more users or roles using\n'
+ 'the `ALL EXCEPT` clause in the `ALTER ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Alter_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Alter.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `ALTER ROW POLICY` statement\n'
+ '\n'
+ '``` sql\n'
+ 'ALTER [ROW] POLICY [IF EXISTS] name [ON CLUSTER cluster_name] ON [database.]table\n'
+ ' [RENAME TO new_name]\n'
+ ' [AS {PERMISSIVE | RESTRICTIVE}]\n'
+ ' [FOR SELECT]\n'
+ ' [USING {condition | NONE}][,...]\n'
+ ' [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Drop = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Drop',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support removing one or more row policies using the `DROP ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Drop_Effect = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Drop.Effect',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL remove checking the condition defined in the row policy\n'
+ 'in all the users and roles to which the row policy was assigned.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Drop_IfExists = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Drop.IfExists',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support using the `IF EXISTS` clause in the `DROP ROW POLICY` statement\n'
+ 'to skip raising an exception when the row policy does not exist.\n'
+ 'If the `IF EXISTS` clause is not specified then an exception SHALL be\n'
+ 'raised if the row policy does not exist.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Drop_On = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Drop.On',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support removing row policy from one or more specified tables\n'
+ 'using the `ON` clause in the `DROP ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Drop_OnCluster = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Drop.OnCluster',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support removing row policy from specified cluster\n'
+ 'using the `ON CLUSTER` clause in the `DROP ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_Drop_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.Drop.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for the `DROP ROW POLICY` statement.\n'
+ '\n'
+ '``` sql\n'
+ 'DROP [ROW] POLICY [IF EXISTS] name [,...] ON [database.]table [,...] [ON CLUSTER cluster_name]\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_ShowCreateRowPolicy = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.ShowCreateRowPolicy',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing the `CREATE ROW POLICY` statement used to create the row policy\n'
+ 'using the `SHOW CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_ShowCreateRowPolicy_On = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.ShowCreateRowPolicy.On',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing statement used to create row policy on specific table \n'
+ 'using the `ON` in the `SHOW CREATE ROW POLICY` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_ShowCreateRowPolicy_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.ShowCreateRowPolicy.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for `SHOW CREATE ROW POLICY`.\n'
+ '\n'
+ '``` sql\n'
+ 'SHOW CREATE [ROW] POLICY name ON [database.]table\n'
+ '```\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_ShowRowPolicies = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.ShowRowPolicies',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing row policies using the `SHOW ROW POLICIES` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_ShowRowPolicies_On = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.ShowRowPolicies.On',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support showing row policies on a specific table\n'
+ 'using the `ON` clause in the `SHOW ROW POLICIES` statement.\n'
+ ),
+ link=None
+ )
+
+RQ_SRS_006_RBAC_RowPolicy_ShowRowPolicies_Syntax = Requirement(
+ name='RQ.SRS-006.RBAC.RowPolicy.ShowRowPolicies.Syntax',
+ version='1.0',
+ priority=None,
+ group=None,
+ type=None,
+ uid=None,
+ description=(
+ '[ClickHouse] SHALL support the following syntax for `SHOW ROW POLICIES`.\n'
+ '\n'
+ '```sql\n'
+ 'SHOW [ROW] POLICIES [ON [database.]table]\n'
+ '```\n'
+ ),
+ link=None
+ )
diff --git a/tests/testflows/rbac/tests/errors.py b/tests/testflows/rbac/tests/errors.py
new file mode 100755
index 00000000000..c1c0988f58b
--- /dev/null
+++ b/tests/testflows/rbac/tests/errors.py
@@ -0,0 +1,86 @@
+## Syntax
+
+# Errors: not found
+
+not_found = "Exception: There is no {type} `{name}` in [disk, users.xml]."
+
+def user_not_found_in_disk(name):
+ return (192,not_found.format(type="user",name=name))
+
+def role_not_found_in_disk(name):
+ return (255,not_found.format(type="role",name=name))
+
+def settings_profile_not_found_in_disk(name):
+ return (180,not_found.format(type="settings profile",name=name))
+
+def quota_not_found_in_disk(name):
+ return (199,not_found.format(type="quota",name=name))
+
+def row_policy_not_found_in_disk(name):
+ return (11,not_found.format(type="row policy",name=name))
+
+# Errors: cannot_rename
+
+cannot_rename = "Exception: {type} `{name}`: cannot rename to `{name_new}` because {type} `{name_new}` already exists in [disk]."
+cannot_rename_exitcode = 237
+
+def cannot_rename_user(name,name_new):
+ return (cannot_rename_exitcode, cannot_rename.format(type="user", name=name, name_new=name_new))
+
+def cannot_rename_role(name,name_new):
+ return (cannot_rename_exitcode, cannot_rename.format(type="role", name=name, name_new=name_new))
+
+def cannot_rename_settings_profile(name,name_new):
+ return (cannot_rename_exitcode, cannot_rename.format(type="settings profile", name=name, name_new=name_new))
+
+def cannot_rename_quota(name,name_new):
+ return (cannot_rename_exitcode, cannot_rename.format(type="quota", name=name, name_new=name_new))
+
+def cannot_rename_row_policy(name,name_new):
+ return (cannot_rename_exitcode, cannot_rename.format(type="row policy", name=name, name_new=name_new))
+
+# Errors: cannot insert
+
+cannot_insert = "Exception: {type} `{name}`: cannot insert because {type} `{name}` already exists in [disk]."
+cannot_insert_exitcode = 237
+
+def cannot_insert_user(name):
+ return (cannot_insert_exitcode, cannot_insert.format(type="user",name=name))
+
+def cannot_insert_role(name):
+ return (cannot_insert_exitcode, cannot_insert.format(type="role",name=name))
+
+def cannot_insert_settings_profile(name):
+ return (cannot_insert_exitcode, cannot_insert.format(type="settings profile",name=name))
+
+def cannot_insert_quota(name):
+ return (cannot_insert_exitcode, cannot_insert.format(type="quota",name=name))
+
+def cannot_insert_row_policy(name):
+ return (cannot_insert_exitcode, cannot_insert.format(type="row policy",name=name))
+
+# Error: default is readonly
+
+default_readonly_exitcode = 239
+cannot_remove_default = "Exception: Cannot remove {type} `default` from [users.xml] because this storage is readonly"
+
+def cannot_update_default():
+ return (default_readonly_exitcode, "Exception: Cannot update user `default` in [users.xml] because this storage is readonly")
+
+def cannot_remove_user_default():
+ return (default_readonly_exitcode, cannot_remove_default.format(type="user"))
+
+def cannot_remove_settings_profile_default():
+ return (default_readonly_exitcode, cannot_remove_default.format(type="settings profile"))
+
+def cannot_remove_quota_default():
+ return (default_readonly_exitcode, cannot_remove_default.format(type="quota"))
+
+# Other syntax errors
+
+def unknown_setting(setting):
+ return (115, f"Exception: Unknown setting {setting}.")
+
+def cluster_not_found(cluster):
+ return (170, f"Exception: Requested cluster '{cluster}' not found.")
+
diff --git a/tests/testflows/rbac/tests/syntax/__init__.py b/tests/testflows/rbac/tests/syntax/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/testflows/rbac/tests/syntax/alter_quota.py b/tests/testflows/rbac/tests/syntax/alter_quota.py
new file mode 100644
index 00000000000..8c47465900b
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/alter_quota.py
@@ -0,0 +1,206 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors
+
+@TestFeature
+@Name("alter quota")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check alter quota query syntax.
+
+ ```sql
+ ALTER QUOTA [IF EXISTS] name [ON CLUSTER cluster_name]
+ [RENAME TO new_name]
+ [KEYED BY {'none' | 'user name' | 'ip address' | 'client key' | 'client key or user name' | 'client key or ip address'}]
+ [FOR [RANDOMIZED] INTERVAL number {SECOND | MINUTE | HOUR | DAY | MONTH}
+ {MAX { {QUERIES | ERRORS | RESULT ROWS | RESULT BYTES | READ ROWS | READ BYTES | EXECUTION TIME} = number } [,...] |
+ NO LIMITS | TRACKING ONLY} [,...]]
+ [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ def cleanup_quota(quota):
+ with Given(f"I ensure that quota {quota} does not exist"):
+ node.query(f"DROP QUOTA IF EXISTS {quota}")
+
+ try:
+ with Given("I have a quota, a user, and a role"):
+ node.query(f"CREATE QUOTA quota0")
+ node.query(f"CREATE USER user0")
+ node.query(f"CREATE ROLE role0")
+
+ with Scenario("I alter quota with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter("1.0")]):
+ with When("I alter quota"):
+ node.query("ALTER QUOTA quota0")
+
+ with Scenario("I alter quota that does not exist, throws an exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter("1.0")]):
+ quota = "quota1"
+ cleanup_quota(quota)
+ with When(f"I alter quota {quota}, which does not exist"):
+ exitcode, message = errors.quota_not_found_in_disk(name=quota)
+ node.query(f"ALTER QUOTA {quota}", exitcode=exitcode, message=message)
+ del quota
+
+ with Scenario("I alter quota with if exists, quota does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_IfExists("1.0")]):
+ node.query("ALTER QUOTA IF EXISTS quota0")
+
+ with Scenario("I alter quota with if exists, quota does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_IfExists("1.0")]):
+ quota = "quota1"
+ cleanup_quota(quota)
+ with When(f"I alter quota {quota}, which does not exist, with IF EXISTS"):
+ node.query(f"ALTER QUOTA IF EXISTS {quota}")
+ del quota
+
+ with Scenario("I alter quota using rename, target available", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Rename("1.0")]):
+ node.query("ALTER QUOTA quota0 RENAME TO quota0")
+
+ with Scenario("I alter quota using rename, target unavailable", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Rename("1.0")]):
+ new_quota = "quota1"
+
+ try:
+ with Given(f"Ensure target name {new_quota} is NOT available"):
+ node.query(f"CREATE QUOTA IF NOT EXISTS {new_quota}")
+
+ with When(f"I try to rename to {new_quota}"):
+ exitcode, message = errors.cannot_rename_quota(name="quota0", name_new=new_quota)
+ node.query(f"ALTER QUOTA quota0 RENAME TO {new_quota}", exitcode=exitcode, message=message)
+ finally:
+ with Finally(f"I cleanup target name {new_quota}"):
+ node.query(f"DROP QUOTA IF EXISTS {new_quota}")
+
+ del new_quota
+
+ keys = ['none', 'user name', 'ip address', 'client key', 'client key or user name', 'client key or ip address']
+ for key in keys:
+ with Scenario(f"I alter quota keyed by {key}", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_KeyedBy("1.0"),
+ RQ_SRS_006_RBAC_Quota_Alter_KeyedByOptions("1.0")]):
+ with When("I alter quota with a key"):
+ node.query(f"ALTER QUOTA quota0 KEYED BY '{key}'")
+
+ with Scenario("I alter quota for randomized interval", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Interval_Randomized("1.0")]):
+ with When("I alter quota on a randomized interval"):
+ node.query("ALTER QUOTA quota0 FOR RANDOMIZED INTERVAL 1 DAY NO LIMITS")
+
+ intervals = ['SECOND', 'MINUTE', 'HOUR', 'DAY', 'MONTH']
+ for i, interval in enumerate(intervals):
+ with Scenario(f"I alter quota for interval {interval}", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Interval("1.0")]):
+ with When(f"I alter quota for {interval}"):
+ node.query(f"ALTER QUOTA quota0 FOR INTERVAL 1 {interval} NO LIMITS")
+
+ constraints = ['MAX QUERIES', 'MAX ERRORS', 'MAX RESULT ROWS',
+ 'MAX RESULT BYTES', 'MAX READ ROWS', 'MAX READ BYTES', 'MAX EXECUTION TIME',
+ 'NO LIMITS', 'TRACKING ONLY']
+ for i, constraint in enumerate(constraints):
+ with Scenario(f"I alter quota for {constraint.lower()}", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Queries("1.0"),
+ RQ_SRS_006_RBAC_Quota_Alter_Errors("1.0"),
+ RQ_SRS_006_RBAC_Quota_Alter_ResultRows("1.0"),
+ RQ_SRS_006_RBAC_Quota_Alter_ReadRows("1.0"),
+ RQ_SRS_006_RBAC_Quota_ALter_ResultBytes("1.0"),
+ RQ_SRS_006_RBAC_Quota_Alter_ReadBytes("1.0"),
+ RQ_SRS_006_RBAC_Quota_Alter_ExecutionTime("1.0"),
+ RQ_SRS_006_RBAC_Quota_Alter_NoLimits("1.0"),
+ RQ_SRS_006_RBAC_Quota_Alter_TrackingOnly("1.0")]):
+ with When("I alter quota for a constraint"):
+ node.query(f"ALTER QUOTA quota0 FOR INTERVAL 1 DAY {constraint}{' 1024' if constraint.startswith('MAX') else ''}")
+
+ with Scenario("I create quota for multiple constraints", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Interval("1.0"),
+ RQ_SRS_006_RBAC_Quota_Alter_Queries("1.0")]):
+ node.query("ALTER QUOTA quota0 \
+ FOR INTERVAL 1 DAY NO LIMITS, \
+ FOR INTERVAL 2 DAY MAX QUERIES 124, \
+ FOR INTERVAL 1 MONTH TRACKING ONLY")
+
+ with Scenario("I alter quota to assign to one role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Assignment("1.0")]):
+ with When("I alter quota to a role"):
+ node.query("ALTER QUOTA quota0 TO role0")
+
+ with Scenario("I alter quota to assign to role that does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Assignment("1.0")]):
+ role = "role1"
+ with Given(f"I drop {role} if it exists"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with Then(f"I alter a quota, assign to role {role}, which does not exist"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"ALTER QUOTA quota0 TO {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I alter quota to assign to all except role that does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Assignment("1.0")]):
+ role = "role1"
+ with Given(f"I drop {role} if it exists"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with Then(f"I alter a quota, assign to all except role {role}, which does not exist"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"ALTER QUOTA quota0 TO ALL EXCEPT {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I alter quota to assign to one role and one user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Assignment("1.0")]):
+ with When("I alter quota to a role and a user"):
+ node.query("ALTER QUOTA quota0 TO role0, user0")
+
+ with Scenario("I alter quota assigned to none", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Assignment_None("1.0")]):
+ with When("I alter quota to none"):
+ node.query("ALTER QUOTA quota0 TO NONE")
+
+ with Scenario("I alter quota to assign to all", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Assignment_All("1.0")]):
+ with When("I alter quota to all"):
+ node.query("ALTER QUOTA quota0 TO ALL")
+
+ with Scenario("I alter quota to assign to all except one role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Assignment_Except("1.0")]):
+ with When("I alter quota to all except one role"):
+ node.query("ALTER QUOTA quota0 TO ALL EXCEPT role0")
+
+ with Scenario("I alter quota to assign to all except multiple roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Assignment_Except("1.0")]):
+ with When("I alter quota to all except one multiple roles"):
+ node.query("ALTER QUOTA quota0 TO ALL EXCEPT role0, user0")
+
+ with Scenario("I alter quota on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Cluster("1.0")]):
+ try:
+ with Given("I have a quota on a cluster"):
+ node.query("CREATE QUOTA quota1 ON CLUSTER sharded_cluster")
+
+ with When("I run alter quota command on a cluster"):
+ node.query("ALTER QUOTA quota1 ON CLUSTER sharded_cluster")
+ with And("I run alter quota command on a cluster with a key"):
+ node.query("ALTER QUOTA quota1 ON CLUSTER sharded_cluster KEYED BY 'none'")
+ with And("I run alter quota command on a cluster with an interval"):
+ node.query("ALTER QUOTA quota1 ON CLUSTER sharded_cluster FOR INTERVAL 1 DAY TRACKING ONLY")
+ with And("I run alter quota command on a cluster for all"):
+ node.query("ALTER QUOTA quota1 ON CLUSTER sharded_cluster TO ALL")
+ finally:
+ with Finally("I drop the quota"):
+ node.query("DROP QUOTA IF EXISTS quota1 ON CLUSTER sharded_cluster")
+
+ with Scenario("I alter quota on nonexistent cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Alter_Cluster("1.0")]):
+ with When("I run alter quota on a cluster"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("ALTER QUOTA quota0 ON CLUSTER fake_cluster", exitcode=exitcode, message=message)
+
+ finally:
+ with Finally("I drop the quota and all the users and roles"):
+ node.query(f"DROP QUOTA IF EXISTS quota0")
+ node.query(f"DROP USER IF EXISTS user0")
+ node.query(f"DROP ROLE IF EXISTS role0")
diff --git a/tests/testflows/rbac/tests/syntax/alter_role.py b/tests/testflows/rbac/tests/syntax/alter_role.py
new file mode 100644
index 00000000000..839fe57c8df
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/alter_role.py
@@ -0,0 +1,196 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("alter role")
+def feature(self, node="clickhouse1"):
+ """Check alter role query syntax.
+
+ ```sql
+ ALTER ROLE [IF EXISTS] name [ON CLUSTER cluster_name]
+ [RENAME TO new_name]
+ [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def setup(role, profile=None):
+ try:
+ with Given("I have a role"):
+ node.query(f"CREATE ROLE OR REPLACE {role}")
+ if profile != None: #create profile when name is given
+ with Given("And I have a profile"):
+ node.query(f"CREATE SETTINGS PROFILE OR REPLACE {profile}")
+ yield
+ finally:
+ with Finally("I drop the role"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ if profile != "":
+ with Finally("I drop the profile"):
+ node.query(f"DROP SETTINGS PROFILE IF EXISTS {profile}")
+
+ def cleanup_role(role):
+ with Given(f"I ensure that role {role} does not exist"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+
+ with Scenario("I alter role with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter("1.0")]):
+ with setup("role0"):
+ with When("I alter role"):
+ node.query("ALTER ROLE role0")
+
+ with Scenario("I alter role that does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter("1.0")]):
+ role = "role0"
+ cleanup_role(role)
+ with When(f"I alter role {role} that does not exist"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"ALTER ROLE {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I alter role if exists, role does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_IfExists("1.0")]):
+ with setup("role1"):
+ with When("I alter role with if exists"):
+ node.query("ALTER ROLE IF EXISTS role1")
+
+ with Scenario("I alter role if exists, role does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_IfExists("1.0")]):
+ role = "role0"
+ cleanup_role(role)
+ with When(f"I alter role {role} that does not exist"):
+ node.query(f"ALTER ROLE IF EXISTS {role}")
+ del role
+
+ with Scenario("I alter role on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Cluster("1.0")]):
+ try:
+ with Given("I have a role on a cluster"):
+ node.query("CREATE ROLE role1 ON CLUSTER sharded_cluster")
+ with When("I run alter role on a cluster"):
+ node.query("ALTER ROLE role1 ON CLUSTER sharded_cluster")
+ with And("I rename role on a cluster"):
+ node.query("ALTER ROLE role1 ON CLUSTER sharded_cluster RENAME TO role2")
+ with And("I alter role with settings on a cluster"):
+ node.query("ALTER ROLE role2 ON CLUSTER sharded_cluster SETTINGS max_memory_usage=10000000 READONLY")
+ finally:
+ with Finally("I drop the role"):
+ node.query("DROP ROLE IF EXISTS role1,role2 ON CLUSTER sharded_cluster")
+
+ with Scenario("I alter role on nonexistent cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Cluster("1.0")]):
+ with When("I run alter role on a cluster"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("ALTER ROLE role1 ON CLUSTER fake_cluster", exitcode=exitcode, message=message)
+
+ with Scenario("I alter role to rename, new name is available", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Rename("1.0")]):
+ with setup("role2"):
+ new_role = "role3"
+ try:
+ with Given(f"Ensure target name {new_role} is available"):
+ node.query(f"DROP ROLE IF EXISTS {new_role}")
+ with When(f"I try to rename to {new_role}"):
+ node.query(f"ALTER ROLE role2 RENAME TO {new_role}")
+ finally:
+ with Finally(f"I cleanup new name {new_role}"):
+ node.query(f"DROP ROLE IF EXISTS {new_role}")
+ del new_role
+
+ with Scenario("I alter role to rename, new name is not available, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Rename("1.0")]):
+ with setup("role2a"):
+ new_role = "role3a"
+ try:
+ with Given(f"Ensure target name {new_role} is NOT available"):
+ node.query(f"CREATE ROLE IF NOT EXISTS {new_role}")
+ with When(f"I try to rename to {new_role}"):
+ exitcode, message = errors.cannot_rename_role(name="role2a", name_new=new_role)
+ node.query(f"ALTER ROLE role2a RENAME TO {new_role}", exitcode=exitcode, message=message)
+ finally:
+ with Finally(f"I cleanup target name {new_role}"):
+ node.query(f"DROP ROLE IF EXISTS {new_role}")
+ del new_role
+
+ with Scenario("I alter role settings profile", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Settings("1.0")]):
+ with setup("role4"):
+ with When("I alter role with settings profile"):
+ node.query("ALTER ROLE role4 SETTINGS PROFILE default, max_memory_usage=10000000 READONLY")
+
+ with Scenario("I alter role settings profile, profile does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Settings("1.0")]):
+ with setup("role4a"):
+ with Given("I ensure profile profile0 does not exist"):
+ node.query("DROP SETTINGS PROFILE IF EXISTS profile0")
+ with When("I alter role with settings profile that does not exist"):
+ exitcode, message = errors.settings_profile_not_found_in_disk("profile0")
+ node.query("ALTER ROLE role4a SETTINGS PROFILE profile0", exitcode=exitcode, message=message)
+
+ with Scenario("I alter role settings profile multiple", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Settings("1.0")]):
+ with setup("role4b", profile="profile0"):
+ with When("I alter role with multiple profiles"):
+ node.query("ALTER ROLE role4b SETTINGS PROFILE default, PROFILE profile0, \
+ max_memory_usage=10000000 READONLY")
+
+ with Scenario("I alter role settings without profile", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Settings("1.0")]):
+ with setup("role5"):
+ with When("I alter role with settings and no profile"):
+ node.query("ALTER ROLE role5 SETTINGS max_memory_usage=10000000 READONLY")
+
+ with Scenario("I alter role settings, variable does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Settings("1.0")]):
+ with setup("role5a"):
+ with When("I alter role using settings and nonexistent value"):
+ exitcode, message = errors.unknown_setting("fake_setting")
+ node.query("ALTER ROLE role5a SETTINGS fake_setting = 100000001", exitcode=exitcode, message=message)
+
+
+ with Scenario("I alter role settings without profile multiple", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Settings("1.0")]):
+ with setup("role6"):
+ with When("I alter role with multiple settings and no profile"):
+ node.query("ALTER ROLE role6 SETTINGS max_memory_usage=10000000 READONLY, \
+ max_rows_to_read MIN 20 MAX 25")
+
+ with Scenario("I alter role settings with multiple profiles multiple variables", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Settings("1.0")]):
+ with setup("role7", profile="profile1"):
+ with When("I alter role with multiple settings and profiles"):
+ node.query("ALTER ROLE role7 SETTINGS PROFILE default, PROFILE profile1, \
+ max_memory_usage=10000000 READONLY, max_rows_to_read MIN 20 MAX 25")
+
+ with Scenario("I alter role settings readonly", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Settings("1.0")]):
+ with setup("role8"):
+ with When("I alter role with readonly"):
+ node.query("ALTER ROLE role8 SETTINGS max_memory_usage READONLY")
+
+ with Scenario("I alter role settings writable", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Settings("1.0")]):
+ with setup("role9"):
+ with When("I alter role with writable"):
+ node.query("ALTER ROLE role9 SETTINGS max_memory_usage WRITABLE")
+
+ with Scenario("I alter role settings min, with and without = sign", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Settings("1.0")]):
+ with setup("role10"):
+ with When("I set min, no equals"):
+ node.query("ALTER ROLE role10 SETTINGS max_memory_usage MIN 200")
+ with When("I set min, yes equals"):
+ node.query("ALTER ROLE role10 SETTINGS max_memory_usage MIN = 200")
+
+ with Scenario("I alter role settings max, with and without = sign", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Alter_Settings("1.0")]):
+ with setup("role11"):
+ with When("I set max, no equals"):
+ node.query("ALTER ROLE role11 SETTINGS max_memory_usage MAX 2000")
+ with When("I set max, yes equals"):
+ node.query("ALTER ROLE role11 SETTINGS max_memory_usage MAX = 200")
\ No newline at end of file
diff --git a/tests/testflows/rbac/tests/syntax/alter_row_policy.py b/tests/testflows/rbac/tests/syntax/alter_row_policy.py
new file mode 100644
index 00000000000..79af04db771
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/alter_row_policy.py
@@ -0,0 +1,244 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("alter row policy")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check alter row policy query syntax.
+
+ ```sql
+ ALTER [ROW] POLICY [IF EXISTS] name [ON CLUSTER cluster_name] ON [database.]table
+ [RENAME TO new_name]
+ [AS {PERMISSIVE | RESTRICTIVE}]
+ [FOR SELECT]
+ [USING {condition | NONE}][,...]
+ [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(policy):
+ try:
+ with Given("I have a row policy"):
+ node.query(f"CREATE ROW POLICY {policy} ON default.foo")
+ yield
+ finally:
+ with Finally("I drop the row policy"):
+ node.query(f"DROP ROW POLICY IF EXISTS {policy} ON default.foo")
+
+ def cleanup_policy(policy):
+ with Given(f"I ensure that policy {policy} does not exist"):
+ node.query(f"DROP ROW POLICY IF EXISTS {policy} ON default.foo")
+
+ try:
+ with Given("I have a table and some roles"):
+ node.query(f"CREATE TABLE default.foo (x UInt64, y String) Engine=Memory")
+ node.query(f"CREATE ROLE role0")
+ node.query(f"CREATE ROLE role1")
+
+ with Scenario("I alter row policy with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy0"):
+ with When("I alter row policy"):
+ node.query("ALTER ROW POLICY policy0 ON default.foo")
+
+ with Scenario("I alter row policy using short syntax with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy1"):
+ with When("I alter row policy short form"):
+ node.query("ALTER POLICY policy1 ON default.foo")
+
+ with Scenario("I alter row policy, does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ policy = "policy2"
+ cleanup_policy(policy)
+ with When(f"I alter row policy {policy} that doesn't exist"):
+ exitcode, message = errors.row_policy_not_found_in_disk(name=f"{policy} ON default.foo")
+ node.query(f"ALTER ROW POLICY {policy} ON default.foo", exitcode=exitcode, message=message)
+ del policy
+
+ with Scenario("I alter row policy if exists", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_IfExists("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy2"):
+ with When("I alter row policy using if exists"):
+ node.query("ALTER ROW POLICY IF EXISTS policy2 ON default.foo")
+
+ with Scenario("I alter row policy if exists, policy does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_IfExists("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ policy = "policy2"
+ cleanup_policy(policy)
+ with When(f"I alter row policy {policy} that doesn't exist"):
+ node.query(f"ALTER ROW POLICY IF EXISTS {policy} ON default.foo")
+ del policy
+
+ with Scenario("I alter row policy to rename, target available", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Rename("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy3"):
+ with When("I alter row policy with rename"):
+ node.query("ALTER ROW POLICY policy3 ON default.foo RENAME TO policy3")
+
+ with Scenario("I alter row policy to rename, target unavailable", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Rename("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy3"):
+ new_policy = "policy4"
+ try:
+ with Given(f"Ensure target name {new_policy} is NOT available"):
+ node.query(f"CREATE ROW POLICY IF NOT EXISTS {new_policy} ON default.foo")
+ with When(f"I try to rename to {new_policy}"):
+ exitcode, message = errors.cannot_rename_row_policy(name="policy3 ON default.foo",
+ name_new=f"{new_policy} ON default.foo")
+ node.query(f"ALTER ROW POLICY policy3 ON default.foo RENAME TO {new_policy}", exitcode=exitcode, message=message)
+ finally:
+ with Finally(f"I cleanup target name {new_policy}"):
+ node.query(f"DROP ROW POLICY IF EXISTS {new_policy} ON default.foo")
+ del new_policy
+
+ with Scenario("I alter row policy to permissive", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Access_Permissive("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy4"):
+ with When("I alter row policy as permissive"):
+ node.query("ALTER ROW POLICY policy4 ON default.foo AS PERMISSIVE")
+
+ with Scenario("I alter row policy to restrictive", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Access_Restrictive("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy5"):
+ with When("I alter row policy as restrictive"):
+ node.query("ALTER ROW POLICY policy5 ON default.foo AS RESTRICTIVE")
+
+ with Scenario("I alter row policy for select", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_ForSelect("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy6"):
+ with When("I alter row policy using for select"):
+ node.query("ALTER ROW POLICY policy6 ON default.foo FOR SELECT USING x > 10")
+
+ with Scenario("I alter row policy using condition", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Condition("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy6"):
+ with When("I alter row policy wtih condition"):
+ node.query("ALTER ROW POLICY policy6 ON default.foo USING x > 10")
+
+ with Scenario("I alter row policy using condition none", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Condition_None("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy7"):
+ with When("I alter row policy using no condition"):
+ node.query("ALTER ROW POLICY policy7 ON default.foo USING NONE")
+
+ with Scenario("I alter row policy to one role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Assignment("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy8"):
+ with When("I alter row policy to a role"):
+ node.query("ALTER ROW POLICY policy8 ON default.foo TO role0")
+
+ with Scenario("I alter row policy to assign to role that does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Assignment("1.0")]):
+ role = "role2"
+ with cleanup("policy8a"):
+ with Given(f"I drop {role} if it exists"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with Then(f"I alter a row policy, assign to role {role}, which does not exist"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"ALTER ROW POLICY policy8a ON default.foo TO {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I alter row policy to assign to all excpet role that does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Assignment("1.0")]):
+ role = "role2"
+ with cleanup("policy8a"):
+ with Given(f"I drop {role} if it exists"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with Then(f"I alter a row policy, assign to all except role {role}, which does not exist"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"ALTER ROW POLICY policy8a ON default.foo TO ALL EXCEPT {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I alter row policy assigned to multiple roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Assignment("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy9"):
+ with When("I alter row policy to multiple roles"):
+ node.query("ALTER ROW POLICY policy9 ON default.foo TO role0, role1")
+
+ with Scenario("I alter row policy assigned to all", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Assignment_All("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy10"):
+ with When("I alter row policy to all"):
+ node.query("ALTER ROW POLICY policy10 ON default.foo TO ALL")
+
+ with Scenario("I alter row policy assigned to all except one role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Assignment_AllExcept("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy11"):
+ with When("I alter row policy to all except"):
+ node.query("ALTER ROW POLICY policy11 ON default.foo TO ALL EXCEPT role0")
+
+ with Scenario("I alter row policy assigned to all except multiple roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Assignment_AllExcept("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy12"):
+ with When("I alter row policy to all except multiple roles"):
+ node.query("ALTER ROW POLICY policy12 ON default.foo TO ALL EXCEPT role0, role1")
+
+ with Scenario("I alter row policy assigned to none", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_Assignment_None("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with cleanup("policy12"):
+ with When("I alter row policy to no assignment"):
+ node.query("ALTER ROW POLICY policy12 ON default.foo TO NONE")
+
+ # Official syntax: ON CLUSTER cluster_name ON database.table
+ # Working syntax: both orderings of ON CLUSTER and TABLE clauses work
+
+ with Scenario("I alter row policy on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_OnCluster("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ try:
+ with Given("I have a row policy"):
+ node.query("CREATE ROW POLICY policy13 ON CLUSTER sharded_cluster ON default.foo")
+ with When("I run alter row policy command"):
+ node.query("ALTER ROW POLICY policy13 ON CLUSTER sharded_cluster ON default.foo")
+ finally:
+ with Finally("I drop the row policy"):
+ node.query("DROP ROW POLICY IF EXISTS policy13 ON CLUSTER sharded_cluster ON default.foo")
+
+ with Scenario("I alter row policy on fake cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_OnCluster("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ with When("I run alter row policy command"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("ALTER ROW POLICY policy13 ON CLUSTER fake_cluster ON default.foo", exitcode=exitcode, message=message)
+
+ with Scenario("I alter row policy on cluster after table", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Alter_OnCluster("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Alter_On("1.0")]):
+ try:
+ with Given("I have a row policy"):
+ node.query("CREATE ROW POLICY policy14 ON default.foo ON CLUSTER sharded_cluster")
+ with When("I run create row policy command"):
+ node.query("ALTER ROW POLICY policy14 ON default.foo ON CLUSTER sharded_cluster")
+ finally:
+ with Finally("I drop the row policy"):
+ node.query("DROP ROW POLICY IF EXISTS policy14 ON default.foo ON CLUSTER sharded_cluster")
+ finally:
+ with Finally("I drop the table and the roles"):
+ node.query(f"DROP TABLE IF EXISTS default.foo")
+ node.query(f"DROP ROLE IF EXISTS role0, role1")
\ No newline at end of file
diff --git a/tests/testflows/rbac/tests/syntax/alter_settings_profile.py b/tests/testflows/rbac/tests/syntax/alter_settings_profile.py
new file mode 100644
index 00000000000..d4ce65da785
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/alter_settings_profile.py
@@ -0,0 +1,232 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("alter settings profile")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check alter settings profile query syntax.
+
+ ```sql
+ ALTER SETTINGS PROFILE [IF EXISTS] name
+ [ON CLUSTER cluster_name]
+ [RENAME TO new_name]
+ [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | INHERIT 'profile_name'] [,...]
+ [TO {user_or_role [,...] | NONE | ALL | ALL EXCEPT user_or_role [,...]]}
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ def cleanup_profile(profile):
+ with Given(f"I ensure that profile {profile} does not exist"):
+ node.query(f"DROP SETTINGS PROFILE IF EXISTS {profile}")
+
+ try:
+ with Given("I have a profile and some users and roles"):
+ node.query(f"CREATE SETTINGS PROFILE profile0")
+ node.query(f"CREATE USER user0")
+ node.query(f"CREATE ROLE role0")
+
+ with Scenario("I alter settings profile with no options", flags=TE, requirements=[RQ_SRS_006_RBAC_SettingsProfile_Alter("1.0")]):
+ with When("I alter settings profile"):
+ node.query("ALTER SETTINGS PROFILE profile0")
+
+ with Scenario("I alter settings profile short form", flags=TE, requirements=[RQ_SRS_006_RBAC_SettingsProfile_Alter("1.0")]):
+ with When("I short form alter settings profile"):
+ node.query("ALTER PROFILE profile0")
+
+ with Scenario("I alter settings profile that does not exist, throws exception", flags=TE, requirements=[RQ_SRS_006_RBAC_SettingsProfile_Alter("1.0")]):
+ profile = "profile1"
+
+ cleanup_profile(profile)
+ with When(f"I alter settings profile {profile} that doesn't exist"):
+ exitcode, message = errors.settings_profile_not_found_in_disk(name=profile)
+ node.query(f"ALTER SETTINGS PROFILE {profile}", exitcode=exitcode, message=message)
+ del profile
+
+ with Scenario("I alter settings profile if exists", flags=TE, requirements=[RQ_SRS_006_RBAC_SettingsProfile_Alter_IfExists("1.0")]):
+ with When("I alter settings profile using if exists"):
+ node.query("ALTER SETTINGS PROFILE IF EXISTS profile0")
+
+ with Scenario("I alter settings profile if exists, profile does not exist", flags=TE, requirements=[RQ_SRS_006_RBAC_SettingsProfile_Alter_IfExists("1.0")]):
+ profile = "profile1"
+
+ cleanup_profile(profile)
+ with When(f"I alter settings profile {profile} using if exists"):
+ node.query(f"ALTER SETTINGS PROFILE IF EXISTS {profile}")
+
+ del profile
+
+ with Scenario("I alter settings profile to rename, target available", flags=TE, requirements=[RQ_SRS_006_RBAC_SettingsProfile_Alter_Rename("1.0")]):
+ with When("I alter settings profile by renaming it"):
+ node.query("ALTER SETTINGS PROFILE profile0 RENAME TO profile0")
+
+ with Scenario("I alter settings profile to rename, target unavailable", flags=TE, requirements=[RQ_SRS_006_RBAC_SettingsProfile_Alter_Rename("1.0")]):
+ new_profile = "profile1"
+
+ try:
+ with Given(f"Ensure target name {new_profile} is NOT available"):
+ node.query(f"CREATE SETTINGS PROFILE IF NOT EXISTS {new_profile}")
+
+ with When(f"I try to rename to {new_profile}"):
+ exitcode, message = errors.cannot_rename_settings_profile(name="profile0", name_new=new_profile)
+ node.query(f"ALTER SETTINGS PROFILE profile0 RENAME TO {new_profile}", exitcode=exitcode, message=message)
+ finally:
+ with Finally(f"I cleanup target name {new_profile}"):
+ node.query(f"DROP SETTINGS PROFILE IF EXISTS {new_profile}")
+
+ del new_profile
+
+ with Scenario("I alter settings profile with a setting value", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables("1.0"),
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables_Value("1.0")]):
+ with When("I alter settings profile using settings"):
+ node.query("ALTER SETTINGS PROFILE profile0 SETTINGS max_memory_usage = 100000001")
+
+ with Scenario("I alter settings profile with a setting value, does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables("1.0"),
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables_Value("1.0")]):
+ with When("I alter settings profile using settings and nonexistent value"):
+ exitcode, message = errors.unknown_setting("fake_setting")
+ node.query("ALTER SETTINGS PROFILE profile0 SETTINGS fake_setting = 100000001", exitcode=exitcode, message=message)
+
+ with Scenario("I alter settings profile with a min setting value", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables_Constraints("1.0")]):
+ with When("I alter settings profile using 2 minimum formats"):
+ node.query("ALTER SETTINGS PROFILE profile0 SETTINGS max_memory_usage MIN 100000001")
+ node.query("ALTER SETTINGS PROFILE profile0 SETTINGS max_memory_usage MIN = 100000001")
+
+ with Scenario("I alter settings profile with a max setting value", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables_Constraints("1.0")]):
+ with When("I alter settings profile using 2 maximum formats"):
+ node.query("ALTER SETTINGS PROFILE profile0 SETTINGS max_memory_usage MAX 100000001")
+ node.query("ALTER SETTINGS PROFILE profile0 SETTINGS max_memory_usage MAX = 100000001")
+
+ with Scenario("I alter settings profile with min and max setting values", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables_Constraints("1.0")]):
+ with When("I alter settings profile with both min and max"):
+ node.query("ALTER SETTINGS PROFILE profile0 SETTINGS max_memory_usage MIN 100000001 MAX 200000001")
+
+ with Scenario("I alter settings profile with a readonly setting", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables_Constraints("1.0")]):
+ with When("I alter settings profile with with readonly"):
+ node.query("ALTER SETTINGS PROFILE profile0 SETTINGS max_memory_usage READONLY")
+
+ with Scenario("I alter settings profile with a writable setting", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables_Constraints("1.0")]):
+ with When("I alter settings profile with writable"):
+ node.query("ALTER SETTINGS PROFILE profile0 SETTINGS max_memory_usage WRITABLE")
+
+ with Scenario("I alter settings profile with inherited settings", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_Inherit("1.0")]):
+ with When("I alter settings profile with inherit"):
+ node.query("ALTER SETTINGS PROFILE profile0 SETTINGS INHERIT 'default'")
+
+ with Scenario("I alter settings profile with inherit, parent profile does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_Inherit("1.0")]):
+ profile = "profile3"
+ with Given(f"I ensure that profile {profile} does not exist"):
+ node.query(f"DROP SETTINGS PROFILE IF EXISTS {profile}")
+ with When("I alter settings profile inherit from nonexistant parent"):
+ exitcode, message = errors.settings_profile_not_found_in_disk(profile)
+ node.query(f"ALTER PROFILE profile0 SETTINGS INHERIT {profile}", exitcode=exitcode, message=message)
+ del profile
+
+ with Scenario("I alter settings profile with multiple settings", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables("1.0"),
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables_Value("1.0")]):
+ with When("I alter settings profile with multiple settings"):
+ node.query("ALTER SETTINGS PROFILE profile0"
+ " SETTINGS max_memory_usage = 100000001"
+ " SETTINGS max_memory_usage_for_user = 100000001")
+
+ with Scenario("I alter settings profile with multiple settings short form", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables("1.0"),
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Variables_Value("1.0")]):
+ with When("I alter settings profile with short form multiple settings"):
+ node.query("ALTER SETTINGS PROFILE profile0"
+ " SETTINGS max_memory_usage = 100000001,"
+ " max_memory_usage_for_user = 100000001")
+
+ with Scenario("I alter settings profile assigned to one role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment("1.0")]):
+ with When("I alter settings profile with assignment to role"):
+ node.query("ALTER SETTINGS PROFILE profile0 TO role0")
+
+ with Scenario("I alter settings profile to assign to role that does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment("1.0")]):
+ role = "role1"
+ with Given(f"I drop {role} if it exists"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with Then(f"I alter a settings profile, assign to role {role}, which does not exist"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"ALTER SETTINGS PROFILE profile0 TO {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I alter settings profile to assign to all except role that does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment("1.0")]):
+ role = "role1"
+ with Given(f"I drop {role} if it exists"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with Then(f"I alter a settings profile, assign to all except role {role}, which does not exist"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"ALTER SETTINGS PROFILE profile0 TO ALL EXCEPT {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I alter settings profile assigned to multiple roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment("1.0")]):
+ with When("I alter settings profile with assignment to multiple roles"):
+ node.query("ALTER SETTINGS PROFILE profile0 TO role0, user0")
+
+ with Scenario("I alter settings profile assigned to all", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_All("1.0")]):
+ with When("I alter settings profile with assignment to all"):
+ node.query("ALTER SETTINGS PROFILE profile0 TO ALL")
+
+ with Scenario("I alter settings profile assigned to all except one role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_AllExcept("1.0")]):
+ with When("I alter settings profile with assignment to all except a role"):
+ node.query("ALTER SETTINGS PROFILE profile0 TO ALL EXCEPT role0")
+
+ with Scenario("I alter settings profile assigned to all except multiple roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_AllExcept("1.0")]):
+ with When("I alter settings profile with assignmentto all except multiple roles"):
+ node.query("ALTER SETTINGS PROFILE profile0 TO ALL EXCEPT role0, user0")
+
+ with Scenario("I alter settings profile assigned to none", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_None("1.0")]):
+ with When("I alter settings profile with assignment to none"):
+ node.query("ALTER SETTINGS PROFILE profile0 TO NONE")
+
+ with Scenario("I alter settings profile on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_OnCluster("1.0")]):
+ try:
+ with Given("I have a settings profile on cluster"):
+ node.query("CREATE SETTINGS PROFILE profile1 ON CLUSTER sharded_cluster")
+ with When("I run alter settings profile command"):
+ node.query("ALTER SETTINGS PROFILE profile1 ON CLUSTER sharded_cluster")
+ with And("I alter settings profile with settings"):
+ node.query("ALTER SETTINGS PROFILE profile1 ON CLUSTER sharded_cluster SETTINGS max_memory_usage = 100000001")
+ with And("I alter settings profile with inherit"):
+ node.query("ALTER SETTINGS PROFILE profile1 ON CLUSTER sharded_cluster SETTINGS INHERIT 'default'")
+ with And("I alter settings profile to all"):
+ node.query("ALTER SETTINGS PROFILE profile1 ON CLUSTER sharded_cluster TO ALL")
+ finally:
+ with Finally("I drop the settings profile"):
+ node.query("DROP SETTINGS PROFILE IF EXISTS profile1 ON CLUSTER sharded_cluster")
+
+ with Scenario("I alter settings profile on fake cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Alter_Assignment_OnCluster("1.0")]):
+ with When("I run alter settings profile command"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("ALTER SETTINGS PROFILE profile1 ON CLUSTER fake_cluster", exitcode=exitcode, message=message)
+
+ finally:
+ with Finally("I drop the profile and all the users and roles"):
+ node.query(f"DROP SETTINGS PROFILE IF EXISTS profile0")
+ node.query(f"DROP USER IF EXISTS user0")
+ node.query(f"DROP ROLE IF EXISTS role0")
diff --git a/tests/testflows/rbac/tests/syntax/alter_user.py b/tests/testflows/rbac/tests/syntax/alter_user.py
new file mode 100644
index 00000000000..aab610c8f79
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/alter_user.py
@@ -0,0 +1,324 @@
+import hashlib
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("alter user")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check alter user query syntax.
+
+ ```sql
+ ALTER USER [IF EXISTS] name [ON CLUSTER cluster_name]
+ [RENAME TO new_name]
+ [IDENTIFIED [WITH {PLAINTEXT_PASSWORD|SHA256_PASSWORD|DOUBLE_SHA1_PASSWORD}] BY {'password'|'hash'}]
+ [[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
+ [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
+ [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def setup(user):
+ try:
+ with Given("I have a user"):
+ node.query(f"CREATE USER OR REPLACE {user}")
+ yield
+ finally:
+ with Finally("I drop the user", flags=TE):
+ node.query(f"DROP USER IF EXISTS {user}")
+
+ with Scenario("I alter user, base command", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter("1.0")]):
+ with setup("user0"):
+ with When("I alter user"):
+ node.query("ALTER USER user0")
+
+ with Scenario("I alter user that does not exist without if exists, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter("1.0")]):
+ with When("I run alter user command, expecting error 192"):
+ exitcode, message = errors.user_not_found_in_disk(name="user0")
+ node.query(f"ALTER USER user0",exitcode=exitcode, message=message)
+
+ with Scenario("I alter user with if exists", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_IfExists("1.0")]):
+ with setup("user0"):
+ with When(f"I alter user with if exists"):
+ node.query(f"ALTER USER IF EXISTS user0")
+
+ with Scenario("I alter user that does not exist with if exists", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_IfExists("1.0")]):
+ user = "user0"
+ with Given("I don't have a user"):
+ node.query(f"DROP USER IF EXISTS {user}")
+ with When(f"I alter user {user} with if exists"):
+ node.query(f"ALTER USER IF EXISTS {user}")
+ del user
+
+ with Scenario("I alter user on a cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Cluster("1.0")]):
+ with Given("I have a user on a cluster"):
+ node.query("CREATE USER OR REPLACE user0 ON CLUSTER sharded_cluster")
+ with When("I alter user on a cluster"):
+ node.query("ALTER USER user0 ON CLUSTER sharded_cluster")
+ with Finally("I drop user from cluster"):
+ node.query("DROP USER IF EXISTS user0 ON CLUSTER sharded_cluster")
+
+ with Scenario("I alter user on a fake cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Cluster("1.0")]):
+ with When("I alter user on a fake cluster"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("ALTER USER user0 ON CLUSTER fake_cluster", exitcode=exitcode, message=message)
+
+ with Scenario("I alter user to rename, target available", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Rename("1.0")]):
+ with setup("user15"):
+ with When("I alter user name"):
+ node.query("ALTER USER user15 RENAME TO user15")
+
+ with Scenario("I alter user to rename, target unavailable", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Rename("1.0")]):
+ with setup("user15"):
+ new_user = "user16"
+ try:
+ with Given(f"Ensure target name {new_user} is NOT available"):
+ node.query(f"CREATE USER IF NOT EXISTS {new_user}")
+ with When(f"I try to rename to {new_user}"):
+ exitcode, message = errors.cannot_rename_user(name="user15", name_new=new_user)
+ node.query(f"ALTER USER user15 RENAME TO {new_user}", exitcode=exitcode, message=message)
+ finally:
+ with Finally(f"I cleanup target name {new_user}"):
+ node.query(f"DROP USER IF EXISTS {new_user}")
+ del new_user
+
+ with Scenario("I alter user password plaintext password", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Password_PlainText("1.0")]):
+ with setup("user1"):
+ with When("I alter user with plaintext password"):
+ node.query("ALTER USER user1 IDENTIFIED WITH PLAINTEXT_PASSWORD BY 'mypassword'", step=When)
+
+ with Scenario("I alter user password to sha256", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Password_Sha256Password("1.0")]):
+ with setup("user2"):
+ with When("I alter user with sha256_password"):
+ password = hashlib.sha256("mypassword".encode("utf-8")).hexdigest()
+ node.query(f"ALTER USER user2 IDENTIFIED WITH SHA256_PASSWORD BY '{password}'",step=When)
+
+ with Scenario("I alter user password to double_sha1_password", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Password_DoubleSha1Password("1.0")]):
+ with setup("user3"):
+ with When("I alter user with double_sha1_password"):
+ def hash(password):
+ return hashlib.sha1(password.encode("utf-8")).hexdigest()
+ password = hash(hash("mypassword"))
+ node.query(f"ALTER USER user3 IDENTIFIED WITH DOUBLE_SHA1_PASSWORD BY '{password}'", step=When)
+
+ with Scenario("I alter user host local", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Host_Local("1.0")]):
+ with setup("user4"):
+ with When("I alter user with host local"):
+ node.query("ALTER USER user4 HOST LOCAL")
+
+ with Scenario("I alter user host name", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Host_Name("1.0")]):
+ with setup("user5"):
+ with When("I alter user with host name"):
+ node.query("ALTER USER user5 HOST NAME 'localhost', NAME 'clickhouse.com'")
+
+ with Scenario("I alter user host regexp", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Host_Regexp("1.0")]):
+ with setup("user6"):
+ with When("I alter user with host regexp"):
+ node.query("ALTER USER user6 HOST REGEXP 'lo..*host', 'lo*host'")
+
+ with Scenario("I alter user host ip", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Host_IP("1.0")]):
+ with setup("user7"):
+ with When("I alter user with host ip"):
+ node.query("ALTER USER user7 HOST IP '127.0.0.1', IP '127.0.0.2'")
+
+ with Scenario("I alter user host like", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Host_Like("1.0")]):
+ with setup("user8"):
+ with When("I alter user with host like"):
+ node.query("ALTER USER user8 HOST LIKE '%.clickhouse.com'")
+
+ with Scenario("I alter user host any", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Host_Any("1.0")]):
+ with setup("user9"):
+ with When("I alter user with host any"):
+ node.query("ALTER USER user9 HOST ANY")
+
+ with Scenario("I alter user host many hosts", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Host_Like("1.0")]):
+ with setup("user11"):
+ with When("I alter user with multiple hosts"):
+ node.query("ALTER USER user11 HOST LIKE '%.clickhouse.com', \
+ IP '127.0.0.2', NAME 'localhost', REGEXP 'lo*host'")
+
+ with Scenario("I alter user default role set to none", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Host_None("1.0")]):
+ with setup("user12"):
+ with When("I alter user with default role none"):
+ node.query("ALTER USER user12 DEFAULT ROLE NONE")
+
+ with Scenario("I alter user default role set to all", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_DefaultRole_All("1.0")]):
+ with setup("user13"):
+ with When("I alter user with all roles set to default"):
+ node.query("ALTER USER user13 DEFAULT ROLE ALL")
+
+ @contextmanager
+ def setup_role(role):
+ try:
+ with Given(f"I have a role {role}"):
+ node.query(f"CREATE ROLE OR REPLACE {role}")
+ yield
+ finally:
+ with Finally(f"I drop the role {role}", flags=TE):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+
+ with Scenario("I alter user default role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_DefaultRole("1.0")]):
+ with setup("user14"), setup_role("role2"):
+ with Given("I have a user with a role"):
+ node.query("GRANT role2 TO user14")
+ with When("I alter user default role"):
+ node.query("ALTER USER user14 DEFAULT ROLE role2")
+
+ with Scenario("I alter user default role, setting default role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_DefaultRole("1.0")]):
+ with setup("user14a"), setup_role("default"):
+ with Given("I grant default role to the user"):
+ node.query("GRANT default TO user14a")
+ with When("I alter user default role"):
+ node.query("ALTER USER user14a DEFAULT ROLE default")
+
+ with Scenario("I alter user default role, role doesn't exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_DefaultRole("1.0")]):
+ with setup("user12"):
+ role = "role0"
+ with Given(f"I ensure that role {role} does not exist"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with When(f"I alter user with default role {role}"):
+ exitcode, message = errors.role_not_found_in_disk(role)
+ node.query(f"ALTER USER user12 DEFAULT ROLE {role}",exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I alter user default role, all except role doesn't exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_DefaultRole("1.0")]):
+ with setup("user12"):
+ role = "role0"
+ with Given(f"I ensure that role {role} does not exist"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with When(f"I alter user with default role {role}"):
+ exitcode, message = errors.role_not_found_in_disk(role)
+ node.query(f"ALTER USER user12 DEFAULT ROLE ALL EXCEPT {role}",exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I alter user default role multiple", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_DefaultRole("1.0")]):
+ with setup("user15"), setup_role("second"), setup_role("third"):
+ with Given("I have a user with multiple roles"):
+ node.query("GRANT second,third TO user15")
+ with When("I alter user default role to second, third"):
+ node.query("ALTER USER user15 DEFAULT ROLE second, third")
+
+ with Scenario("I alter user default role set to all except", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_DefaultRole_AllExcept("1.0")]):
+ with setup("user16"), setup_role("second"):
+ with Given("I have a user with a role"):
+ node.query("GRANT second TO user16")
+ with When("I alter user default role"):
+ node.query("ALTER USER user16 DEFAULT ROLE ALL EXCEPT second")
+
+ with Scenario("I alter user default role multiple all except", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_DefaultRole_AllExcept("1.0")]):
+ with setup("user17"), setup_role("second"), setup_role("third"):
+ with Given("I have a user with multiple roles"):
+ node.query("GRANT second,third TO user17")
+ with When("I alter user default role to all except second"):
+ node.query("ALTER USER user17 DEFAULT ROLE ALL EXCEPT second")
+
+ with Scenario("I alter user settings profile", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Settings("1.0"), \
+ RQ_SRS_006_RBAC_User_Alter_Settings_Profile("1.0")]):
+ with setup("user18"):
+ try:
+ with Given("I have a profile"):
+ node.query(f"CREATE SETTINGS PROFILE profile10")
+ with When("I alter user with settings and set profile to profile1"):
+ node.query("ALTER USER user18 SETTINGS PROFILE profile10, max_memory_usage = 100 MIN 0 MAX 1000 READONLY")
+ finally:
+ with Finally("I drop the profile"):
+ node.query(f"DROP SETTINGS PROFILE profile10")
+
+ with Scenario("I alter user settings profile, fake profile, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Settings("1.0"),
+ RQ_SRS_006_RBAC_User_Alter_Settings_Profile("1.0")]):
+ with setup("user18a"):
+ profile = "profile0"
+ with Given(f"I ensure that profile {profile} does not exist"):
+ node.query(f"DROP SETTINGS PROFILE IF EXISTS {profile}")
+ with When(f"I alter user with Settings and set profile to fake profile {profile}"):
+ exitcode, message = errors.settings_profile_not_found_in_disk(profile)
+ node.query("ALTER USER user18a SETTINGS PROFILE profile0", exitcode=exitcode, message=message)
+ del profile
+
+ with Scenario("I alter user settings with a fake setting, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Settings("1.0")]):
+ with setup("user18b"):
+ with When("I alter settings profile using settings and nonexistent value"):
+ exitcode, message = errors.unknown_setting("fake_setting")
+ node.query("ALTER USER user18b SETTINGS fake_setting = 100000001", exitcode=exitcode, message=message)
+
+ with Scenario("I alter user settings without profile (no equals)", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Settings("1.0"),
+ RQ_SRS_006_RBAC_User_Alter_Settings_Min("1.0"),
+ RQ_SRS_006_RBAC_User_Alter_Settings_Max("1.0")]):
+ with setup("user19"):
+ with When("I alter user with settings without profile using no equals"):
+ node.query("ALTER USER user19 SETTINGS max_memory_usage=10000000 MIN 100000 MAX 1000000000 READONLY")
+
+ #equals sign (=) syntax verify
+ with Scenario("I alter user settings without profile (yes equals)", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Settings("1.0"),
+ RQ_SRS_006_RBAC_User_Alter_Settings_Min("1.0"),
+ RQ_SRS_006_RBAC_User_Alter_Settings_Max("1.0")]):
+ with setup("user20"):
+ with When("I alter user with settings without profile using equals"):
+ node.query("ALTER USER user20 SETTINGS max_memory_usage=10000000 MIN=100000 MAX=1000000000 READONLY")
+
+ #Add requirement to host: add/drop
+ with Scenario("I alter user to add host", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Host_AddDrop("1.0")]):
+ with setup("user21"):
+ with When("I alter user by adding local host"):
+ node.query("ALTER USER user21 ADD HOST LOCAL")
+ with And("I alter user by adding no host"):
+ node.query("ALTER USER user21 ADD HOST NONE")
+ with And("I alter user by adding host like"):
+ node.query("ALTER USER user21 ADD HOST LIKE 'local%'")
+ with And("I alter user by adding host ip"):
+ node.query("ALTER USER user21 ADD HOST IP '127.0.0.1'")
+ with And("I alter user by adding host name"):
+ node.query("ALTER USER user21 ADD HOST NAME 'localhost'")
+
+ with Scenario("I alter user to remove host", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Alter_Host_AddDrop("1.0")]):
+ with setup("user22"):
+ with When("I alter user by removing local host"):
+ node.query("ALTER USER user22 DROP HOST LOCAL")
+ with And("I alter user by removing no host"):
+ node.query("ALTER USER user22 DROP HOST NONE")
+ with And("I alter user by removing like host"):
+ node.query("ALTER USER user22 DROP HOST LIKE 'local%'")
+ with And("I alter user by removing host ip"):
+ node.query("ALTER USER user22 DROP HOST IP '127.0.0.1'")
+ with And("I alter user by removing host name"):
+ node.query("ALTER USER user22 DROP HOST NAME 'localhost'")
diff --git a/tests/testflows/rbac/tests/syntax/create_quota.py b/tests/testflows/rbac/tests/syntax/create_quota.py
new file mode 100644
index 00000000000..9697da2ac29
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/create_quota.py
@@ -0,0 +1,227 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("create quota")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check create quota query syntax.
+
+ ```sql
+ CREATE QUOTA [IF NOT EXISTS | OR REPLACE] name [ON CLUSTER cluster_name]
+ [KEYED BY {'none' | 'user name' | 'ip address' | 'client key' | 'client key or user name' | 'client key or ip address'}]
+ [FOR [RANDOMIZED] INTERVAL number {SECOND | MINUTE | HOUR | DAY}
+ {MAX { {QUERIES | ERRORS | RESULT ROWS | RESULT BYTES | READ ROWS | READ BYTES | EXECUTION TIME} = number } [,...] |
+ NO LIMITS | TRACKING ONLY} [,...]]
+ [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(quota):
+ try:
+ with Given("I ensure the quota does not already exist"):
+ node.query(f"DROP QUOTA IF EXISTS {quota}")
+ yield
+ finally:
+ with Finally("I drop the quota"):
+ node.query(f"DROP QUOTA IF EXISTS {quota}")
+
+ def create_quota(quota):
+ with And(f"I ensure I do have quota {quota}"):
+ node.query(f"CREATE QUOTA OR REPLACE {quota}")
+
+ try:
+ with Given("I have a user and a role"):
+ node.query(f"CREATE USER user0")
+ node.query(f"CREATE ROLE role0")
+
+ with Scenario("I create quota with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create("1.0")]):
+ with cleanup("quota0"):
+ with When("I create a quota with no options"):
+ node.query("CREATE QUOTA quota0")
+
+ with Scenario("I create quota that already exists, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create("1.0")]):
+ quota = "quota0"
+ with cleanup(quota):
+ create_quota(quota)
+ with When(f"I create a quota {quota} that already exists without IF EXISTS, throws exception"):
+ exitcode, message = errors.cannot_insert_quota(name=quota)
+ node.query(f"CREATE QUOTA {quota}", exitcode=exitcode, message=message)
+ del quota
+
+ with Scenario("I create quota if not exists, quota does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_IfNotExists("1.0")]):
+ quota = "quota1"
+ with cleanup(quota):
+ with When(f"I create a quota {quota} with if not exists"):
+ node.query(f"CREATE QUOTA IF NOT EXISTS {quota}")
+ del quota
+
+ with Scenario("I create quota if not exists, quota does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_IfNotExists("1.0")]):
+ quota = "quota1"
+ with cleanup(quota):
+ create_quota(quota)
+ with When(f"I create a quota {quota} with if not exists"):
+ node.query(f"CREATE QUOTA IF NOT EXISTS {quota}")
+ del quota
+
+ with Scenario("I create quota or replace, quota does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Replace("1.0")]):
+ quota = "quota2"
+ with cleanup(quota):
+ with When(f"I create a quota {quota} with or replace"):
+ node.query(f"CREATE QUOTA OR REPLACE {quota}")
+ del quota
+
+ with Scenario("I create quota or replace, quota does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Replace("1.0")]):
+ quota = "quota2"
+ with cleanup(quota):
+ create_quota(quota)
+ with When(f"I create a quota {quota} with or replace"):
+ node.query(f"CREATE QUOTA OR REPLACE {quota}")
+ del quota
+
+ keys = ['none', 'user name', 'ip address', 'client key', 'client key or user name', 'client key or ip address']
+ for i, key in enumerate(keys):
+ with Scenario(f"I create quota keyed by {key}", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_KeyedBy("1.0"),
+ RQ_SRS_006_RBAC_Quota_Create_KeyedByOptions("1.0")]):
+ name = f'quota{3 + i}'
+ with cleanup(name):
+ with When(f"I create a quota with {key}"):
+ node.query(f"CREATE QUOTA {name} KEYED BY '{key}'")
+
+ with Scenario("I create quota for randomized interval", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Interval_Randomized("1.0")]):
+ with cleanup("quota9"):
+ with When("I create a quota for randomized interval"):
+ node.query("CREATE QUOTA quota9 FOR RANDOMIZED INTERVAL 1 DAY NO LIMITS")
+
+ intervals = ['SECOND', 'MINUTE', 'HOUR', 'DAY', 'MONTH']
+ for i, interval in enumerate(intervals):
+ with Scenario(f"I create quota for interval {interval}", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Interval("1.0")]):
+ name = f'quota{10 + i}'
+ with cleanup(name):
+ with When(f"I create a quota for {interval} interval"):
+ node.query(f"CREATE QUOTA {name} FOR INTERVAL 1 {interval} NO LIMITS")
+
+ constraints = ['MAX QUERIES', 'MAX ERRORS', 'MAX RESULT ROWS',
+ 'MAX RESULT BYTES', 'MAX READ ROWS', 'MAX READ BYTES', 'MAX EXECUTION TIME',
+ 'NO LIMITS', 'TRACKING ONLY']
+ for i, constraint in enumerate(constraints):
+ with Scenario(f"I create quota for {constraint.lower()}", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Queries("1.0"),
+ RQ_SRS_006_RBAC_Quota_Create_Errors("1.0"),
+ RQ_SRS_006_RBAC_Quota_Create_ResultRows("1.0"),
+ RQ_SRS_006_RBAC_Quota_Create_ResultBytes("1.0"),
+ RQ_SRS_006_RBAC_Quota_Create_ReadRows("1.0"),
+ RQ_SRS_006_RBAC_Quota_Create_ReadBytes("1.0"),
+ RQ_SRS_006_RBAC_Quota_Create_ExecutionTime("1.0"),
+ RQ_SRS_006_RBAC_Quota_Create_NoLimits("1.0"),
+ RQ_SRS_006_RBAC_Quota_Create_TrackingOnly("1.0")]):
+ name = f'quota{15 + i}'
+ with cleanup(name):
+ with When(f"I create quota for {constraint.lower()}"):
+ node.query(f"CREATE QUOTA {name} FOR INTERVAL 1 DAY {constraint}{' 1024' if constraint.startswith('MAX') else ''}")
+
+ with Scenario("I create quota for multiple constraints", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Interval("1.0"),
+ RQ_SRS_006_RBAC_Quota_Create_Queries("1.0")]):
+ with cleanup("quota23"):
+ with When(f"I create quota for multiple constraints"):
+ node.query('CREATE QUOTA quota23 \
+ FOR INTERVAL 1 DAY NO LIMITS, \
+ FOR INTERVAL 2 DAY MAX QUERIES 124, \
+ FOR INTERVAL 1 HOUR TRACKING ONLY')
+
+ with Scenario("I create quota assigned to one role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Assignment("1.0")]):
+ with cleanup("quota24"):
+ with When("I create quota for role"):
+ node.query("CREATE QUOTA quota24 TO role0")
+
+ with Scenario("I create quota to assign to role that does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Assignment("1.0")]):
+ role = "role1"
+ with Given(f"I drop {role} if it exists"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with Then(f"I create a quota, assign to role {role}, which does not exist"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"CREATE QUOTA quota0 TO {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I create quota to assign to all except role that does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Assignment("1.0")]):
+ role = "role1"
+ with Given(f"I drop {role} if it exists"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with Then(f"I create a quota, assign to all except role {role}, which does not exist"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"CREATE QUOTA quota0 TO ALL EXCEPT {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I create quota assigned to no role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Assignment_None("1.0")]):
+ with When("I create quota for no role"):
+ node.query("CREATE QUOTA quota24 TO NONE")
+
+ with Scenario("I create quota assigned to multiple roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Assignment("1.0")]):
+ with cleanup("quota25"):
+ with When("I create quota for multiple roles"):
+ node.query("CREATE QUOTA quota25 TO role0, user0")
+
+ with Scenario("I create quota assigned to all", flags=TE,requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Assignment_All("1.0")]):
+ with cleanup("quota26"):
+ with When("I create quota for all"):
+ node.query("CREATE QUOTA quota26 TO ALL")
+
+ with Scenario("I create quota assigned to all except one role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Assignment_Except("1.0")]):
+ with cleanup("quota27"):
+ with When("I create quota for all except one role"):
+ node.query("CREATE QUOTA quota27 TO ALL EXCEPT role0")
+
+ with Scenario("I create quota assigned to all except multiple roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Assignment_Except("1.0")]):
+ with cleanup("quota28"):
+ with When("I create quota for all except multiple roles"):
+ node.query("CREATE QUOTA quota28 TO ALL EXCEPT role0, user0")
+
+ with Scenario("I create quota on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Cluster("1.0")]):
+ try:
+ with When("I run create quota command on cluster"):
+ node.query("CREATE QUOTA quota29 ON CLUSTER sharded_cluster")
+ with When("I run create quota command on cluster, keyed"):
+ node.query("CREATE QUOTA OR REPLACE quota29 ON CLUSTER sharded_cluster KEYED BY 'none'")
+ with When("I run create quota command on cluster, interval"):
+ node.query("CREATE QUOTA OR REPLACE quota29 ON CLUSTER sharded_cluster FOR INTERVAL 1 DAY TRACKING ONLY")
+ with When("I run create quota command on cluster, assign"):
+ node.query("CREATE QUOTA OR REPLACE quota29 ON CLUSTER sharded_cluster TO ALL")
+ finally:
+ with Finally("I drop the quota from cluster"):
+ node.query("DROP QUOTA IF EXISTS quota29 ON CLUSTER sharded_cluster")
+
+ with Scenario("I create quota on nonexistent cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Create_Cluster("1.0")]):
+ with When("I run create quota on a cluster"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("CREATE QUOTA quota0 ON CLUSTER fake_cluster", exitcode=exitcode, message=message)
+
+ finally:
+ with Finally("I drop all the users and roles"):
+ node.query(f"DROP USER IF EXISTS user0")
+ node.query(f"DROP ROLE IF EXISTS role0")
\ No newline at end of file
diff --git a/tests/testflows/rbac/tests/syntax/create_role.py b/tests/testflows/rbac/tests/syntax/create_role.py
new file mode 100644
index 00000000000..f87710f992b
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/create_role.py
@@ -0,0 +1,122 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("create role")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check create role query syntax.
+
+ ```sql
+ CREATE ROLE [IF NOT EXISTS | OR REPLACE] name
+ [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(role):
+ try:
+ with Given("I ensure the role doesn't already exist"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ yield
+ finally:
+ with Finally("I drop the role"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+
+ def create_role(role):
+ with Given(f"I ensure I do have role {role}"):
+ node.query(f"CREATE ROLE OR REPLACE {role}")
+
+ with Scenario("I create role with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Create("1.0")]):
+ with cleanup("role0"):
+ with When("I create role"):
+ node.query("CREATE ROLE role0")
+
+ with Scenario("I create role that already exists, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Create("1.0")]):
+ role = "role0"
+ with cleanup(role):
+ with When(f"I create role {role}"):
+ exitcode, message = errors.cannot_insert_role(name=role)
+ node.query(f"CREATE ROLE {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I create role if not exists, role does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Create_IfNotExists("1.0")]):
+ role = "role1"
+ with cleanup(role):
+ with When(f"I create role {role} with if not exists"):
+ node.query(f"CREATE ROLE IF NOT EXISTS {role}")
+ del role
+
+ with Scenario("I create role if not exists, role does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Create_IfNotExists("1.0")]):
+ role = "role1"
+ with cleanup(role):
+ create_role(role)
+ with When(f"I create role {role} with if not exists"):
+ node.query(f"CREATE ROLE IF NOT EXISTS {role}")
+ del role
+
+ with Scenario("I create role or replace, role does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Create_Replace("1.0")]):
+ role = "role2"
+ with cleanup(role):
+ with When(f"I create role {role} with or replace"):
+ node.query(f"CREATE ROLE OR REPLACE {role}")
+ del role
+
+ with Scenario("I create role or replace, role does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Create_Replace("1.0")]):
+ role = "role2"
+ with cleanup(role):
+ create_role(role)
+ with When(f"I create role {role} with or replace"):
+ node.query(f"CREATE ROLE OR REPLACE {role}")
+ del role
+
+ with Scenario("I create role on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Create("1.0")]):
+ try:
+ with When("I have a role on a cluster"):
+ node.query("CREATE ROLE role1 ON CLUSTER sharded_cluster")
+ with And("I run create role or replace on a cluster"):
+ node.query("CREATE ROLE OR REPLACE role1 ON CLUSTER sharded_cluster")
+ with And("I create role with settings on a cluster"):
+ node.query("CREATE ROLE role2 ON CLUSTER sharded_cluster SETTINGS max_memory_usage=10000000 READONLY")
+ finally:
+ with Finally("I drop the role"):
+ node.query("DROP ROLE IF EXISTS role1,role2 ON CLUSTER sharded_cluster")
+
+ with Scenario("I create role on nonexistent cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Create("1.0")]):
+ with When("I run create role on a cluster"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("CREATE ROLE role1 ON CLUSTER fake_cluster", exitcode=exitcode, message=message)
+
+ with Scenario("I create role with settings profile", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Create_Settings("1.0")]):
+ with cleanup("role3"):
+ with When("I create role with settings profile"):
+ node.query("CREATE ROLE role3 SETTINGS PROFILE default, max_memory_usage=10000000 WRITABLE")
+
+ with Scenario("I create role settings profile, fake profile, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Create_Settings("1.0")]):
+ with cleanup("role4a"):
+ with Given("I ensure profile profile0 does not exist"):
+ node.query("DROP SETTINGS PROFILE IF EXISTS profile0")
+ with When("I create role with settings profile that does not exist"):
+ exitcode, message = errors.settings_profile_not_found_in_disk("profile0")
+ node.query("CREATE ROLE role4a SETTINGS PROFILE profile0", exitcode=exitcode, message=message)
+
+ with Scenario("I create role with settings without profile", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Create_Settings("1.0")]):
+ with cleanup("role4"):
+ with When("I create role with settings without profile"):
+ node.query("CREATE ROLE role4 SETTINGS max_memory_usage=10000000 READONLY")
diff --git a/tests/testflows/rbac/tests/syntax/create_row_policy.py b/tests/testflows/rbac/tests/syntax/create_row_policy.py
new file mode 100644
index 00000000000..458b205e6c1
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/create_row_policy.py
@@ -0,0 +1,225 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("create row policy")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check create row policy query syntax.
+
+ ```sql
+ CREATE [ROW] POLICY [IF NOT EXISTS | OR REPLACE] policy_name [ON CLUSTER cluster_name] ON [db.]table
+ [AS {PERMISSIVE | RESTRICTIVE}]
+ [FOR SELECT]
+ [USING condition]
+ [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(policy, on="default.foo"):
+ try:
+ with Given(f"I ensure the row policy does not already exist on {on}"):
+ node.query(f"DROP ROW POLICY IF EXISTS {policy} ON {on}")
+ yield
+ finally:
+ with Finally(f"I drop the row policy on {on}"):
+ node.query(f"DROP ROW POLICY IF EXISTS {policy} ON {on}")
+
+ def create_policy(policy, on="default.foo"):
+ with Given(f"I ensure I do have policy {policy} on {on}"):
+ node.query(f"CREATE ROW POLICY OR REPLACE {policy} ON {on}")
+
+ try:
+ with Given("I have a table and some roles"):
+ node.query(f"CREATE TABLE default.foo (x UInt64, y String) Engine=Memory")
+ node.query(f"CREATE ROLE role0")
+ node.query(f"CREATE ROLE role1")
+
+ with Scenario("I create row policy with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with cleanup("policy0"):
+ with When("I create row policy"):
+ node.query("CREATE ROW POLICY policy0 ON default.foo")
+
+ with Scenario("I create row policy using short syntax with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with cleanup("policy1"):
+ with When("I create row policy short form"):
+ node.query("CREATE POLICY policy1 ON default.foo")
+
+ with Scenario("I create row policy that already exists, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ policy = "policy0"
+ with cleanup(policy):
+ create_policy(policy)
+ with When(f"I create row policy {policy}"):
+ exitcode, message = errors.cannot_insert_row_policy(name=f"{policy} ON default.foo")
+ node.query(f"CREATE ROW POLICY {policy} ON default.foo", exitcode=exitcode, message=message)
+ del policy
+
+ with Scenario("I create row policy if not exists, policy does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_IfNotExists("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with cleanup("policy2"):
+ with When("I create row policy with if not exists"):
+ node.query("CREATE ROW POLICY IF NOT EXISTS policy2 ON default.foo")
+
+ with Scenario("I create row policy if not exists, policy does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_IfNotExists("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ policy = "policy2"
+ with cleanup(policy):
+ create_policy(policy)
+ with When(f"I create row policy {policy} with if not exists"):
+ node.query(f"CREATE ROW POLICY IF NOT EXISTS {policy} ON default.foo")
+ del policy
+
+ with Scenario("I create row policy or replace, policy does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_Replace("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with cleanup("policy3"):
+ with When("I create row policy with or replace"):
+ node.query("CREATE ROW POLICY OR REPLACE policy3 ON default.foo")
+
+ with Scenario("I create row policy or replace, policy does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_Replace("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ policy = "policy3"
+ with cleanup(policy):
+ create_policy(policy)
+ with When(f"I create row policy {policy} with or replace"):
+ node.query(f"CREATE ROW POLICY OR REPLACE {policy} ON default.foo")
+ del policy
+
+ with Scenario("I create row policy as permissive", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_Access_Permissive("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with cleanup("policy4"):
+ with When("I create row policy as permissive"):
+ node.query("CREATE ROW POLICY policy4 ON default.foo AS PERMISSIVE")
+
+ with Scenario("I create row policy as restrictive", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_Access_Restrictive("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with cleanup("policy5"):
+ with When("I create row policy as restrictive"):
+ node.query("CREATE ROW POLICY policy5 ON default.foo AS RESTRICTIVE")
+
+ with Scenario("I create row policy for select", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_ForSelect("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_Condition("1.0")]):
+ with cleanup("policy6"):
+ with When("I create row policy with for select"):
+ node.query("CREATE ROW POLICY policy6 ON default.foo FOR SELECT USING x > 10")
+
+ with Scenario("I create row policy using condition", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_Condition("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with cleanup("policy6"):
+ with When("I create row policy with condition"):
+ node.query("CREATE ROW POLICY policy6 ON default.foo USING x > 10")
+
+ with Scenario("I create row policy assigned to one role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_Assignment("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with cleanup("policy7"):
+ with When("I create row policy for one role"):
+ node.query("CREATE ROW POLICY policy7 ON default.foo TO role0")
+
+ with Scenario("I create row policy to assign to role that does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_Assignment("1.0")]):
+ role = "role2"
+ with cleanup("policy8a"):
+ with Given(f"I drop {role} if it exists"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with Then(f"I create a row policy, assign to role {role}, which does not exist"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"CREATE ROW POLICY policy8a ON default.foo TO {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I create row policy to assign to all excpet role that does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_Assignment("1.0")]):
+ role = "role2"
+ with cleanup("policy8a"):
+ with Given(f"I drop {role} if it exists"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with Then(f"I create a row policy, assign to all except role {role}, which does not exist"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"CREATE ROW POLICY policy8a ON default.foo TO ALL EXCEPT {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I create row policy assigned to multiple roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_Assignment("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with cleanup("policy8b"):
+ with When("I create row policy for multiple roles"):
+ node.query("CREATE ROW POLICY policy8b ON default.foo TO role0, role1")
+
+ with Scenario("I create row policy assigned to all", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_Assignment_All("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with cleanup("policy9"):
+ with When("I create row policy for all"):
+ node.query("CREATE ROW POLICY policy9 ON default.foo TO ALL")
+
+ with Scenario("I create row policy assigned to all except one role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_Assignment_AllExcept("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with cleanup("policy10"):
+ with When("I create row policy for all except one"):
+ node.query("CREATE ROW POLICY policy10 ON default.foo TO ALL EXCEPT role0")
+
+ with Scenario("I create row policy assigned to all except multiple roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_Assignment_AllExcept("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with cleanup("policy11"):
+ with When("I create row policy for all except multiple roles"):
+ node.query("CREATE ROW POLICY policy11 ON default.foo TO ALL EXCEPT role0, role1")
+
+ with Scenario("I create row policy assigned to none", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_Assignment_None("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with cleanup("policy11"):
+ with When("I create row policy for none"):
+ node.query("CREATE ROW POLICY policy11 ON default.foo TO NONE")
+
+ with Scenario("I create row policy on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_OnCluster("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ try:
+ with When("I run create row policy command on cluster"):
+ node.query("CREATE ROW POLICY policy12 ON CLUSTER sharded_cluster ON default.foo")
+ finally:
+ with Finally("I drop the row policy from cluster"):
+ node.query("DROP ROW POLICY IF EXISTS policy12 ON default.foo ON CLUSTER sharded_cluster")
+
+ with Scenario("I create row policy on fake cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_OnCluster("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ with When("I run create row policy command"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("CREATE ROW POLICY policy13 ON CLUSTER fake_cluster ON default.foo", exitcode=exitcode, message=message)
+
+ with Scenario("I create row policy on cluster after table", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Create_OnCluster("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Create_On("1.0")]):
+ try:
+ with When("I run create row policy command on cluster"):
+ node.query("CREATE ROW POLICY policy12 ON default.foo ON CLUSTER sharded_cluster")
+ finally:
+ with Finally("I drop the row policy from cluster"):
+ node.query("DROP ROW POLICY IF EXISTS policy12 ON default.foo ON CLUSTER sharded_cluster")
+ finally:
+ with Finally("I drop the table and the roles"):
+ node.query(f"DROP TABLE IF EXISTS default.foo")
+ node.query(f"DROP ROLE IF EXISTS role0, role1")
\ No newline at end of file
diff --git a/tests/testflows/rbac/tests/syntax/create_settings_profile.py b/tests/testflows/rbac/tests/syntax/create_settings_profile.py
new file mode 100644
index 00000000000..1cd5289db26
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/create_settings_profile.py
@@ -0,0 +1,254 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("create settings profile")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check create settings profile query syntax.
+
+ ```sql
+ CREATE [SETTINGS] PROFILE [IF NOT EXISTS | OR REPLACE] name [ON CLUSTER cluster_name]
+ [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value]
+ [READONLY] | [INHERIT|PROFILE 'profile_name']] [,...]
+ [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(profile):
+ try:
+ with Given(f"I ensure the profile {profile} does not exist"):
+ node.query(f"DROP SETTINGS PROFILE IF EXISTS {profile}")
+ yield
+ finally:
+ with Finally("I drop the profile"):
+ node.query(f"DROP SETTINGS PROFILE IF EXISTS {profile}")
+
+ def create_profile(profile):
+ with Given(f"I ensure I do have profile {profile}"):
+ node.query(f"CREATE SETTINGS PROFILE OR REPLACE {profile}")
+
+ try:
+ with Given("I have a user and a role"):
+ node.query(f"CREATE USER user0")
+ node.query(f"CREATE ROLE role0")
+
+ with Scenario("I create settings profile with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create("1.0")]):
+ with cleanup("profile0"):
+ with When("I create settings profile"):
+ node.query("CREATE SETTINGS PROFILE profile0")
+
+ with Scenario("I create settings profile that already exists, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create("1.0")]):
+ profile = "profile0"
+ with cleanup(profile):
+ create_profile(profile)
+ with When(f"I create settings profile {profile} that already exists"):
+ exitcode, message = errors.cannot_insert_settings_profile(name=profile)
+ node.query(f"CREATE SETTINGS PROFILE {profile}", exitcode=exitcode, message=message)
+ del profile
+
+ with Scenario("I create settings profile if not exists, profile does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_IfNotExists("1.0")]):
+ with cleanup("profile1"):
+ with When("I create settings profile with if not exists"):
+ node.query("CREATE SETTINGS PROFILE IF NOT EXISTS profile1")
+
+ with Scenario("I create settings profile if not exists, profile does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_IfNotExists("1.0")]):
+ profile = "profile1"
+ with cleanup(profile):
+ create_profile(profile)
+ with When(f"I create settings profile {profile} with if not exists"):
+ node.query(f"CREATE SETTINGS PROFILE IF NOT EXISTS {profile}")
+ del profile
+
+ with Scenario("I create settings profile or replace, profile does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Replace("1.0")]):
+ with cleanup("profile2"):
+ with When("I create settings policy with or replace"):
+ node.query("CREATE SETTINGS PROFILE OR REPLACE profile2")
+
+ with Scenario("I create settings profile or replace, profile does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Replace("1.0")]):
+ with cleanup("profile2"):
+ create_profile("profile2")
+ with When("I create settings policy with or replace"):
+ node.query("CREATE SETTINGS PROFILE OR REPLACE profile2")
+
+ with Scenario("I create settings profile short form", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create("1.0")]):
+ with cleanup("profile3"):
+ with When("I create settings profile short form"):
+ node.query("CREATE PROFILE profile3")
+
+ with Scenario("I create settings profile with a setting value", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Variables("1.0"),
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Variables_Value("1.0")]):
+ with cleanup("profile4"):
+ with When("I create settings profile with settings"):
+ node.query("CREATE SETTINGS PROFILE profile4 SETTINGS max_memory_usage = 100000001")
+
+ with Scenario("I create settings profile with a setting value, does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Variables("1.0"),
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Variables_Value("1.0")]):
+ with When("I create settings profile using settings and nonexistent value"):
+ exitcode, message = errors.unknown_setting("fake_setting")
+ node.query("CREATE SETTINGS PROFILE profile0 SETTINGS fake_setting = 100000001", exitcode=exitcode, message=message)
+
+ with Scenario("I create settings profile with a min setting value", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Variables_Constraints("1.0")]):
+ with cleanup("profile5"), cleanup("profile6"):
+ with When("I create settings profile with min setting with and without equals"):
+ node.query("CREATE SETTINGS PROFILE profile5 SETTINGS max_memory_usage MIN 100000001")
+ node.query("CREATE SETTINGS PROFILE profile6 SETTINGS max_memory_usage MIN = 100000001")
+
+ with Scenario("I create settings profile with a max setting value", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Variables_Constraints("1.0")]):
+ with cleanup("profile7"), cleanup("profile8"):
+ with When("I create settings profile with max setting with and without equals"):
+ node.query("CREATE SETTINGS PROFILE profile7 SETTINGS max_memory_usage MAX 100000001")
+ node.query("CREATE SETTINGS PROFILE profile8 SETTINGS max_memory_usage MAX = 100000001")
+
+ with Scenario("I create settings profile with min and max setting values", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Variables_Constraints("1.0")]):
+ with cleanup("profile9"):
+ with When("I create settings profile with min and max setting"):
+ node.query("CREATE SETTINGS PROFILE profile9 SETTINGS max_memory_usage MIN 100000001 MAX 200000001")
+
+ with Scenario("I create settings profile with a readonly setting", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Variables_Constraints("1.0")]):
+ with cleanup("profile10"):
+ with When("I create settings profile with readonly"):
+ node.query("CREATE SETTINGS PROFILE profile10 SETTINGS max_memory_usage READONLY")
+
+ with Scenario("I create settings profile with a writable setting", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Variables_Constraints("1.0")]):
+ with cleanup("profile21"):
+ with When("I create settings profile with writable"):
+ node.query("CREATE SETTINGS PROFILE profile21 SETTINGS max_memory_usage WRITABLE")
+
+ with Scenario("I create settings profile with inherited settings", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Inherit("1.0")]):
+ with cleanup("profile11"):
+ with When("I create settings profile with inherit"):
+ node.query("CREATE SETTINGS PROFILE profile11 SETTINGS INHERIT 'default'")
+
+ with Scenario("I create settings profile with inherit/from profile, fake profile, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Inherit("1.0")]):
+ profile = "profile3"
+ with Given(f"I ensure that profile {profile} does not exist"):
+ node.query(f"DROP SETTINGS PROFILE IF EXISTS {profile}")
+ sources = {"INHERIT","PROFILE"}
+ for source in sources:
+ with When(f"I create settings profile {source} from nonexistant parent"):
+ exitcode, message = errors.settings_profile_not_found_in_disk(profile)
+ node.query(f"CREATE PROFILE profile0 SETTINGS {source} {profile}", exitcode=exitcode, message=message)
+ del profile
+
+ with Scenario("I create settings profile with inherited settings other form", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Inherit("1.0")]):
+ with cleanup("profile12"):
+ with When("I create settings profile with inherit short form"):
+ node.query("CREATE PROFILE profile12 SETTINGS PROFILE 'default'")
+
+ with Scenario("I create settings profile with multiple settings", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Variables_Constraints("1.0")]):
+ with cleanup("profile13"):
+ with When("I create settings profile with multiple settings"):
+ node.query("CREATE SETTINGS PROFILE profile13"
+ " SETTINGS max_memory_usage = 100000001"
+ " SETTINGS max_memory_usage_for_user = 100000001")
+
+ with Scenario("I create settings profile with multiple settings short form", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Variables_Constraints("1.0")]):
+ with cleanup("profile14"):
+ with When("I create settings profile with multiple settings short form"):
+ node.query("CREATE SETTINGS PROFILE profile14"
+ " SETTINGS max_memory_usage = 100000001,"
+ " max_memory_usage_for_user = 100000001")
+
+ with Scenario("I create settings profile assigned to one role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Assignment("1.0")]):
+ with cleanup("profile15"):
+ with When("I create settings profile for a role"):
+ node.query("CREATE SETTINGS PROFILE profile15 TO role0")
+
+ with Scenario("I create settings profile to assign to role that does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Assignment("1.0")]):
+ role = "role1"
+ with Given(f"I drop {role} if it exists"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with Then(f"I create a settings profile, assign to role {role}, which does not exist"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"CREATE SETTINGS PROFILE profile0 TO {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I create settings profile to assign to all except role that does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Assignment("1.0")]):
+ role = "role1"
+ with Given(f"I drop {role} if it exists"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with Then(f"I create a settings profile, assign to all except role {role}, which does not exist"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"CREATE SETTINGS PROFILE profile0 TO ALL EXCEPT {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I create settings profile assigned to multiple roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Assignment("1.0")]):
+ with cleanup("profile16"):
+ with When("I create settings profile for multiple roles"):
+ node.query("CREATE SETTINGS PROFILE profile16 TO role0, user0")
+
+ with Scenario("I create settings profile assigned to all", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Assignment_All("1.0")]):
+ with cleanup("profile17"):
+ with When("I create settings profile for all"):
+ node.query("CREATE SETTINGS PROFILE profile17 TO ALL")
+
+ with Scenario("I create settings profile assigned to all except one role", flags=TE,requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Assignment_AllExcept("1.0")]):
+ with cleanup("profile18"):
+ with When("I create settings profile for all except one role"):
+ node.query("CREATE SETTINGS PROFILE profile18 TO ALL EXCEPT role0")
+
+ with Scenario("I create settings profile assigned to all except multiple roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Assignment_AllExcept("1.0")]):
+ with cleanup("profile19"):
+ with When("I create settings profile for all except multiple roles"):
+ node.query("CREATE SETTINGS PROFILE profile19 TO ALL EXCEPT role0, user0")
+
+ with Scenario("I create settings profile assigned to none", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_Assignment_None("1.0")]):
+ with cleanup("profile22"):
+ with When("I create settings profile for none"):
+ node.query("CREATE SETTINGS PROFILE profile22 TO NONE")
+
+ with Scenario("I create settings profile on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_OnCluster("1.0")]):
+ try:
+ with When("I run create settings profile command"):
+ node.query("CREATE SETTINGS PROFILE profile20 ON CLUSTER sharded_cluster")
+ node.query("CREATE SETTINGS PROFILE OR REPLACE profile20 ON CLUSTER sharded_cluster SETTINGS max_memory_usage = 100000001")
+ node.query("CREATE SETTINGS PROFILE OR REPLACE profile20 ON CLUSTER sharded_cluster SETTINGS INHERIT 'default'")
+ node.query("CREATE SETTINGS PROFILE OR REPLACE profile20 ON CLUSTER sharded_cluster TO ALL")
+ finally:
+ with Finally("I drop the settings profile"):
+ node.query("DROP SETTINGS PROFILE IF EXISTS profile20 ON CLUSTER sharded_cluster")
+
+ with Scenario("I create settings profile on fake cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Create_OnCluster("1.0")]):
+ with When("I run create settings profile command"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("CREATE SETTINGS PROFILE profile1 ON CLUSTER fake_cluster", exitcode=exitcode, message=message)
+ finally:
+ with Finally("I drop all the users and roles"):
+ node.query(f"DROP USER IF EXISTS user0")
+ node.query(f"DROP ROLE IF EXISTS role0")
diff --git a/tests/testflows/rbac/tests/syntax/create_user.py b/tests/testflows/rbac/tests/syntax/create_user.py
new file mode 100644
index 00000000000..874421f2c81
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/create_user.py
@@ -0,0 +1,271 @@
+import hashlib
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("create user")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check create user query syntax.
+
+ ```sql
+ CREATE USER [IF NOT EXISTS | OR REPLACE] name [ON CLUSTER cluster_name]
+ [IDENTIFIED [WITH {NO_PASSWORD|PLAINTEXT_PASSWORD|SHA256_PASSWORD|SHA256_HASH|DOUBLE_SHA1_PASSWORD|DOUBLE_SHA1_HASH}] BY {'password'|'hash'}]
+ [HOST {LOCAL | NAME 'name' | NAME REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
+ [DEFAULT ROLE role [,...]]
+ [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(user):
+ try:
+ with Given("I ensure the user does not already exist", flags=TE):
+ node.query(f"DROP USER IF EXISTS {user}")
+ yield
+ finally:
+ with Finally("I drop the user", flags=TE):
+ node.query(f"DROP USER IF EXISTS {user}")
+
+ def create_user(user):
+ with Given(f"I ensure I do have user {user}"):
+ node.query(f"CREATE USER OR REPLACE {user}")
+
+ with Scenario("I create user with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create("1.0"),
+ RQ_SRS_006_RBAC_User_Create_Host_Default("1.0")]):
+ with cleanup("user0"):
+ with When("I create a user with no options"):
+ node.query("CREATE USER user0")
+
+ with Scenario("I create user that already exists, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create("1.0"),
+ RQ_SRS_006_RBAC_User_Create_Host_Default("1.0")]):
+ user = "user0"
+ with cleanup(user):
+ create_user(user)
+ with When(f"I create a user {user} that already exists without IF EXISTS, throws exception"):
+ exitcode, message = errors.cannot_insert_user(name=user)
+ node.query(f"CREATE USER {user}", exitcode=exitcode, message=message)
+ del user
+
+ with Scenario("I create user with if not exists, user does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_IfNotExists("1.0")]):
+ user = "user0"
+ with cleanup(user):
+ with When(f"I create a user {user} with if not exists"):
+ node.query(f"CREATE USER IF NOT EXISTS {user}")
+ del user
+
+ #Bug exists, mark as xfail
+ with Scenario("I create user with if not exists, user does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_IfNotExists("1.0")]):
+ user = "user0"
+ with cleanup(user):
+ create_user(user)
+ with When(f"I create a user {user} with if not exists"):
+ node.query(f"CREATE USER IF NOT EXISTS {user}")
+ del user
+
+ with Scenario("I create user or replace, user does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Replace("1.0")]):
+ user = "user0"
+ with cleanup(user):
+ with When(f"I create a user {user} with or replace"):
+ node.query(f"CREATE USER OR REPLACE {user}")
+ del user
+
+ with Scenario("I create user or replace, user does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Replace("1.0")]):
+ user = "user0"
+ with cleanup(user):
+ create_user(user)
+ with When(f"I create a user {user} with or replace"):
+ node.query(f"CREATE USER OR REPLACE {user}")
+ del user
+
+ with Scenario("I create user with no password", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Password_NoPassword("1.0")]):
+ with cleanup("user1"):
+ with When("I create a user with no password"):
+ node.query("CREATE USER user1 IDENTIFIED WITH NO_PASSWORD")
+
+ with Scenario("I create user with plaintext password", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Password_PlainText("1.0")]):
+ with cleanup("user1"):
+ with When("I create a user with plaintext password"):
+ node.query("CREATE USER user1 IDENTIFIED WITH PLAINTEXT_PASSWORD BY 'mypassword'")
+
+ with Scenario("I create user with sha256 password", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Password_Sha256Password("1.0")]):
+ with cleanup("user2"):
+ with When("I create a user with sha256 password"):
+ password = hashlib.sha256("mypassword".encode("utf-8")).hexdigest()
+ node.query(f"CREATE USER user2 IDENTIFIED WITH SHA256_PASSWORD BY '{password}'")
+
+ with Scenario("I create user with sha256 password using IDENTIFIED BY", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Password_Sha256Password("1.0")]):
+ with cleanup("user2"):
+ with When("I create a user with sha256 password using short form"):
+ password = hashlib.sha256("mypassword".encode("utf-8")).hexdigest()
+ node.query(f"CREATE USER user2 IDENTIFIED BY '{password}'")
+
+ with Scenario("I create user with sha256_hash password", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Password_Sha256Hash("1.0")]):
+ with cleanup("user3"):
+ with When("I create a user with sha256_hash"):
+ def hash(password):
+ return hashlib.sha256(password.encode("utf-8")).hexdigest()
+ password = hash(hash("mypassword"))
+ node.query(f"CREATE USER user3 IDENTIFIED WITH SHA256_HASH BY '{password}'")
+
+ with Scenario("I create user with double sha1 password", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Password_DoubleSha1Password("1.0")]):
+ with cleanup("user3"):
+ with When("I create a user with double_sha1_password"):
+ node.query(f"CREATE USER user3 IDENTIFIED WITH DOUBLE_SHA1_PASSWORD BY 'mypassword'")
+
+ with Scenario("I create user with double sha1 hash", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Password_DoubleSha1Hash("1.0")]):
+ with cleanup("user3"):
+ with When("I create a user with double_sha1_hash"):
+ def hash(password):
+ return hashlib.sha1(password.encode("utf-8")).hexdigest()
+ password = hash(hash("mypassword"))
+ node.query(f"CREATE USER user3 IDENTIFIED WITH DOUBLE_SHA1_HASH BY '{password}'")
+
+ with Scenario("I create user with host name", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Host_Name("1.0")]):
+ with cleanup("user4"):
+ with When("I create a user with host name"):
+ node.query("CREATE USER user4 HOST NAME 'localhost', NAME 'clickhouse.com'")
+
+ with Scenario("I create user with host regexp", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Host_Regexp("1.0")]):
+ with cleanup("user5"):
+ with When("I create a user with host regexp"):
+ node.query("CREATE USER user5 HOST REGEXP 'lo.?*host', REGEXP 'lo*host'")
+
+ with Scenario("I create user with host ip", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Host_IP("1.0")]):
+ with cleanup("user6"):
+ with When("I create a user with host ip"):
+ node.query("CREATE USER user6 HOST IP '127.0.0.1', IP '127.0.0.2'")
+
+ with Scenario("I create user with host like", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Host_Like("1.0")]):
+ with cleanup("user7"):
+ with When("I create a user with host like"):
+ node.query("CREATE USER user7 HOST LIKE 'local%'")
+
+ with Scenario("I create user with host none", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Host_None("1.0")]):
+ with cleanup("user7"):
+ with When("I create a user with host none"):
+ node.query("CREATE USER user7 HOST NONE")
+
+ with Scenario("I create user with host local", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Host_Local("1.0")]):
+ with cleanup("user7"):
+ with When("I create a user with host local"):
+ node.query("CREATE USER user7 HOST LOCAL")
+
+ with Scenario("I create user with host any", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Host_Any("1.0")]):
+ with cleanup("user7"):
+ with When("I create a user with host any"):
+ node.query("CREATE USER user7 HOST ANY")
+
+ with Scenario("I create user with default role set to none", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_DefaultRole_None("1.0")]):
+ with cleanup("user8"):
+ with When("I create a user with no default role"):
+ node.query("CREATE USER user8 DEFAULT ROLE NONE")
+
+ with Scenario("I create user with default role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_DefaultRole("1.0")]):
+ with Given("I have a role"):
+ node.query("CREATE ROLE default")
+ with cleanup("user9"):
+ with When("I create a user with a default role"):
+ node.query("CREATE USER user9 DEFAULT ROLE default")
+ with Finally("I drop the role"):
+ node.query("DROP ROLE default")
+
+ with Scenario("I create user default role, role doesn't exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_DefaultRole("1.0")]):
+ with cleanup("user12"):
+ role = "role0"
+ with Given(f"I ensure that role {role} does not exist"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with When(f"I create user with default role {role}"):
+ exitcode, message = errors.role_not_found_in_disk(role)
+ node.query(f"CREATE USER user12 DEFAULT ROLE {role}",exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I create user default role, all except role doesn't exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_DefaultRole("1.0")]):
+ with cleanup("user12"):
+ role = "role0"
+ with Given(f"I ensure that role {role} does not exist"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with When(f"I create user with default role {role}"):
+ exitcode, message = errors.role_not_found_in_disk(role)
+ node.query(f"CREATE USER user12 DEFAULT ROLE ALL EXCEPT {role}",exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I create user with all roles set to default", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_DefaultRole_All("1.0")]):
+ with cleanup("user10"):
+ with When("I create a user with all roles as default"):
+ node.query("CREATE USER user10 DEFAULT ROLE ALL")
+
+ with Scenario("I create user with settings profile", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Settings("1.0")]):
+ with cleanup("user11"):
+ with When("I create a user with a settings profile"):
+ node.query("CREATE USER user11 SETTINGS PROFILE default, max_memory_usage=10000000 READONLY")
+
+ with Scenario("I create user settings profile, fake profile, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Settings("1.0")]):
+ with cleanup("user18a"):
+ profile = "profile0"
+ with Given(f"I ensure that profile {profile} does not exist"):
+ node.query(f"DROP SETTINGS PROFILE IF EXISTS {profile}")
+ with When(f"I create user with Settings and set profile to fake profile {profile}"):
+ exitcode, message = errors.settings_profile_not_found_in_disk(profile)
+ node.query("CREATE USER user18a SETTINGS PROFILE profile0", exitcode=exitcode, message=message)
+ del profile
+
+ with Scenario("I create user settings with a fake setting, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Settings("1.0")]):
+ with cleanup("user18b"):
+ with When("I create settings profile using settings and nonexistent value"):
+ exitcode, message = errors.unknown_setting("fake_setting")
+ node.query("CREATE USER user18b SETTINGS fake_setting = 100000001", exitcode=exitcode, message=message)
+
+ with Scenario("I create user with settings without profile", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_Settings("1.0")]):
+ with cleanup("user12"):
+ with When("I create a user with settings and no profile"):
+ node.query("CREATE USER user12 SETTINGS max_memory_usage=10000000 READONLY")
+
+ with Scenario("I create user on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_OnCluster("1.0")]):
+ try:
+ with When("I create user on cluster"):
+ node.query("CREATE USER user13 ON CLUSTER sharded_cluster")
+ finally:
+ with Finally("I drop the user"):
+ node.query("DROP USER user13 ON CLUSTER sharded_cluster")
+
+ with Scenario("I create user on fake cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Create_OnCluster("1.0")]):
+ with When("I create user on fake cluster"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("CREATE USER user14 ON CLUSTER fake_cluster", exitcode=exitcode, message=message)
diff --git a/tests/testflows/rbac/tests/syntax/drop_quota.py b/tests/testflows/rbac/tests/syntax/drop_quota.py
new file mode 100644
index 00000000000..a90294800c2
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/drop_quota.py
@@ -0,0 +1,87 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("drop quota")
+def feature(self, node="clickhouse1"):
+ """Check drop quota query syntax.
+
+ ```sql
+ DROP QUOTA [IF EXISTS] name [,...] [ON CLUSTER cluster_name]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(quota):
+ try:
+ with Given("I have a quota"):
+ node.query(f"CREATE QUOTA {quota}")
+ yield
+ finally:
+ with Finally("I drop the quota"):
+ node.query(f"DROP QUOTA IF EXISTS {quota}")
+
+ def cleanup_quota(quota):
+ with Given(f"I ensure that quota {quota} does not exist"):
+ node.query(f"DROP QUOTA IF EXISTS {quota}")
+
+ with Scenario("I drop quota with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Drop("1.0")]):
+ with cleanup("quota0"):
+ with When("I run drop quota command"):
+ node.query("DROP QUOTA quota0")
+
+ with Scenario("I drop quota, does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Drop("1.0")]):
+ quota = "quota0"
+ cleanup_quota(quota)
+ with When("I run drop quota command, throws exception"):
+ exitcode, message = errors.quota_not_found_in_disk(name=quota)
+ node.query(f"DROP QUOTA {quota}", exitcode=exitcode, message=message)
+ del quota
+
+ with Scenario("I drop quota if exists, quota exists", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Drop_IfExists("1.0")]):
+ with cleanup("quota1"):
+ with When("I run drop quota command"):
+ node.query("DROP QUOTA IF EXISTS quota1")
+
+ with Scenario("I drop quota if exists, quota does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Drop_IfExists("1.0")]):
+ cleanup_quota("quota2")
+ with When("I run drop quota command, quota does not exist"):
+ node.query("DROP QUOTA IF EXISTS quota2")
+
+ with Scenario("I drop default quota, throws error", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Drop("1.0")]):
+ with When("I drop default quota"):
+ exitcode, message = errors.cannot_remove_quota_default()
+ node.query("DROP QUOTA default", exitcode=exitcode, message=message)
+
+ with Scenario("I drop multiple quotas", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Drop("1.0")]):
+ with cleanup("quota2"), cleanup("quota3"):
+ with When("I run drop quota command"):
+ node.query("DROP QUOTA quota2, quota3")
+
+ with Scenario("I drop quota on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Drop_Cluster("1.0")]):
+ try:
+ with Given("I have a quota"):
+ node.query("CREATE QUOTA quota4 ON CLUSTER sharded_cluster")
+ with When("I run drop quota command"):
+ node.query("DROP QUOTA quota4 ON CLUSTER sharded_cluster")
+ finally:
+ with Finally("I drop the quota in case it still exists"):
+ node.query("DROP QUOTA IF EXISTS quota4 ON CLUSTER sharded_cluster")
+
+ with Scenario("I drop quota on fake cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_Drop_Cluster("1.0")]):
+ with When("I run drop quota command"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("DROP QUOTA quota5 ON CLUSTER fake_cluster", exitcode=exitcode, message=message)
diff --git a/tests/testflows/rbac/tests/syntax/drop_role.py b/tests/testflows/rbac/tests/syntax/drop_role.py
new file mode 100644
index 00000000000..e3f89298a50
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/drop_role.py
@@ -0,0 +1,84 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("drop role")
+def feature(self, node="clickhouse1"):
+ """Check drop role query syntax.
+
+ ```sql
+ DROP ROLE [IF EXISTS] name [,...] [ON CLUSTER cluster_name]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def setup(role):
+ try:
+ with Given("I have a role"):
+ node.query(f"CREATE ROLE OR REPLACE {role}")
+ yield
+ finally:
+ with Finally("I confirm the role is dropped"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+
+ def cleanup_role(role):
+ with Given(f"I ensure that role {role} does not exist"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+
+
+ with Scenario("I drop role with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Drop("1.0")]):
+ with setup("role0"):
+ with When("I drop role"):
+ node.query("DROP ROLE role0")
+
+ with Scenario("I drop role that doesn't exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Drop("1.0")]):
+ role = "role0"
+ cleanup_role(role)
+ with When(f"I drop role {role}"):
+ exitcode, message = errors.role_not_found_in_disk(name=role)
+ node.query(f"DROP ROLE {role}", exitcode=exitcode, message=message)
+ del role
+
+ with Scenario("I drop multiple roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Drop("1.0")]):
+ with setup("role1"), setup("role2"):
+ with When("I drop multiple roles"):
+ node.query("DROP ROLE role1, role2")
+
+ with Scenario("I drop role that does not exist, using if exists", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Drop_IfExists("1.0")]):
+ with When("I drop role if exists"):
+ node.query("DROP ROLE IF EXISTS role3")
+
+ with Scenario("I drop multiple roles where one does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Drop_IfExists("1.0")]):
+ with setup("role5"):
+ with When("I drop multiple roles where one doesnt exist"):
+ node.query("DROP ROLE IF EXISTS role3, role5")
+
+ with Scenario("I drop multiple roles where both do not exist", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Drop_IfExists("1.0")]):
+ with Given("I ensure role does not exist"):
+ node.query("DROP ROLE IF EXISTS role6")
+ with When("I drop the nonexistant roles"):
+ node.query("DROP USER IF EXISTS role5, role6")
+
+ with Scenario("I drop role on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Drop_Cluster("1.0")]):
+ with Given("I have a role on cluster"):
+ node.query("CREATE ROLE role0 ON CLUSTER sharded_cluster")
+ with When("I drop the role from the cluster"):
+ node.query("DROP ROLE role0 ON CLUSTER sharded_cluster")
+
+ with Scenario("I drop role on fake cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_Drop_Cluster("1.0")]):
+ with When("I run drop role command"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("DROP ROLE role2 ON CLUSTER fake_cluster", exitcode=exitcode, message=message)
diff --git a/tests/testflows/rbac/tests/syntax/drop_row_policy.py b/tests/testflows/rbac/tests/syntax/drop_row_policy.py
new file mode 100644
index 00000000000..37831ea9d27
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/drop_row_policy.py
@@ -0,0 +1,135 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("drop row policy")
+def feature(self, node="clickhouse1"):
+ """Check drop row policy query syntax.
+
+ ```sql
+ DROP [ROW] POLICY [IF EXISTS] name [,...] ON [database.]table [,...] [ON CLUSTER cluster_name]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(policy, on=["default.foo"]):
+ try:
+ with Given("I have a row policy"):
+ for i in policy:
+ for j in on:
+ node.query(f"CREATE ROW POLICY OR REPLACE {i} ON {j}")
+ yield
+ finally:
+ with Finally("I drop the row policy"):
+ for i in policy:
+ for j in on:
+ node.query(f"DROP ROW POLICY IF EXISTS {i} ON {j}")
+
+ def cleanup_policy(policy, on="default.foo"):
+ with Given(f"I ensure that policy {policy} does not exist"):
+ node.query(f"DROP ROW POLICY IF EXISTS {policy} ON {on}")
+
+ try:
+ with Given("I have some tables"):
+ node.query(f"CREATE TABLE default.foo (x UInt64, y String) Engine=Memory")
+ node.query(f"CREATE TABLE default.foo2 (x UInt64, y String) Engine=Memory")
+
+ with Scenario("I drop row policy with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Drop("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Drop_On("1.0")]):
+ with cleanup(["policy1"]):
+ with When("I drop row policy"):
+ node.query("DROP ROW POLICY policy1 ON default.foo")
+
+ with Scenario("I drop row policy using short syntax with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Drop("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Drop_On("1.0")]):
+ with cleanup(["policy2"]):
+ with When("I drop row policy short form"):
+ node.query("DROP POLICY policy2 ON default.foo")
+
+ with Scenario("I drop row policy, does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Drop("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Drop_On("1.0")]):
+ policy = "policy1"
+ cleanup_policy(policy)
+ with When("I drop row policy"):
+ exitcode, message = errors.row_policy_not_found_in_disk(name=f"{policy} ON default.foo")
+ node.query(f"DROP ROW POLICY {policy} ON default.foo", exitcode=exitcode, message=message)
+ del policy
+
+ with Scenario("I drop row policy if exists, policy does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Drop_IfExists("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Drop_On("1.0")]):
+ with cleanup(["policy3"]):
+ with When("I drop row policy if exists"):
+ node.query("DROP ROW POLICY IF EXISTS policy3 ON default.foo")
+
+ with Scenario("I drop row policy if exists, policy doesn't exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Drop_IfExists("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Drop_On("1.0")]):
+ cleanup_policy("policy3")
+ with When("I drop row policy if exists"):
+ node.query("DROP ROW POLICY IF EXISTS policy3 ON default.foo")
+
+ with Scenario("I drop multiple row policies", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Drop("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Drop_On("1.0")]):
+ with cleanup(["policy3", "policy4"]):
+ with When("I drop multiple row policies"):
+ node.query("DROP ROW POLICY policy3, policy4 ON default.foo")
+
+ with Scenario("I drop row policy on multiple tables", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Drop("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Drop_On("1.0")]):
+ with cleanup(["policy3"], ["default.foo","default.foo2"]):
+ with When("I drop row policy on multiple tables"):
+ node.query("DROP ROW POLICY policy3 ON default.foo, default.foo2")
+
+ with Scenario("I drop multiple row policies on multiple tables", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Drop("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Drop_On("1.0")]):
+ with cleanup(["policy3", "policy4"], ["default.foo","default.foo2"]):
+ with When("I drop the row policies from the tables"):
+ node.query("DROP ROW POLICY policy3 ON default.foo, policy4 ON default.foo2")
+
+ with Scenario("I drop row policy on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Drop_OnCluster("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Drop_On("1.0")]):
+ try:
+ with Given("I have a row policy"):
+ node.query("CREATE ROW POLICY policy13 ON default.foo ON CLUSTER sharded_cluster")
+ with When("I run drop row policy command"):
+ node.query("DROP ROW POLICY IF EXISTS policy13 ON CLUSTER sharded_cluster ON default.foo")
+ finally:
+ with Finally("I drop the row policy in case it still exists"):
+ node.query("DROP ROW POLICY IF EXISTS policy13 ON default.foo ON CLUSTER sharded_cluster")
+
+ with Scenario("I drop row policy on cluster after table", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Drop_OnCluster("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Drop_On("1.0")]):
+ try:
+ with Given("I have a row policy"):
+ node.query("CREATE ROW POLICY policy12 ON default.foo ON CLUSTER sharded_cluster")
+ with When("I run drop row policy command"):
+ node.query("DROP ROW POLICY IF EXISTS policy13 ON default.foo ON CLUSTER sharded_cluster")
+ finally:
+ with Finally("I drop the row policy in case it still exists"):
+ node.query("DROP ROW POLICY IF EXISTS policy12 ON default.foo ON CLUSTER sharded_cluster")
+
+ with Scenario("I drop row policy on fake cluster throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_Drop_OnCluster("1.0"),
+ RQ_SRS_006_RBAC_RowPolicy_Drop_On("1.0")]):
+ with When("I run drop row policy command"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("DROP ROW POLICY IF EXISTS policy14 ON default.foo ON CLUSTER fake_cluster",
+ exitcode=exitcode, message=message)
+ finally:
+ with Finally("I drop the tables"):
+ node.query(f"DROP TABLE IF EXISTS default.foo")
+ node.query(f"DROP TABLE IF EXISTS default.foo2")
diff --git a/tests/testflows/rbac/tests/syntax/drop_settings_profile.py b/tests/testflows/rbac/tests/syntax/drop_settings_profile.py
new file mode 100644
index 00000000000..aea5194c9c9
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/drop_settings_profile.py
@@ -0,0 +1,93 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("drop settings profile")
+def feature(self, node="clickhouse1"):
+ """Check drop settings profile query syntax.
+
+ ```sql
+ DROP [SETTINGS] PROFILE [IF EXISTS] name [,...] [ON CLUSTER cluster_name]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(profile):
+ try:
+ with Given("I have a settings profile"):
+ node.query(f"CREATE SETTINGS PROFILE {profile}")
+ yield
+ finally:
+ with Finally("I drop the settings profile"):
+ node.query(f"DROP SETTINGS PROFILE IF EXISTS {profile}")
+
+ def cleanup_profile(profile):
+ with Given(f"I ensure that profile {profile} does not exist"):
+ node.query(f"DROP SETTINGS PROFILE IF EXISTS {profile}")
+
+ with Scenario("I drop settings profile with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Drop("1.0")]):
+ with cleanup("profile0"):
+ with When("I drop settings profile"):
+ node.query("DROP SETTINGS PROFILE profile0")
+
+ with Scenario("I drop settings profile, does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Drop("1.0")]):
+ profile = "profile0"
+ cleanup_profile(profile)
+ with When("I drop settings profile"):
+ exitcode, message = errors.settings_profile_not_found_in_disk(name=profile)
+ node.query("DROP SETTINGS PROFILE profile0", exitcode=exitcode, message=message)
+ del profile
+
+ with Scenario("I drop settings profile short form", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Drop("1.0")]):
+ with cleanup("profile1"):
+ with When("I drop settings profile short form"):
+ node.query("DROP PROFILE profile1")
+
+ with Scenario("I drop settings profile if exists, profile does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Drop_IfExists("1.0")]):
+ with cleanup("profile2"):
+ with When("I drop settings profile if exists"):
+ node.query("DROP SETTINGS PROFILE IF EXISTS profile2")
+
+ with Scenario("I drop settings profile if exists, profile does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Drop_IfExists("1.0")]):
+ cleanup_profile("profile2")
+ with When("I drop settings profile if exists"):
+ node.query("DROP SETTINGS PROFILE IF EXISTS profile2")
+
+ with Scenario("I drop default settings profile, throws error", requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Drop("1.0")]):
+ with When("I drop default profile"):
+ exitcode, message = errors.cannot_remove_settings_profile_default()
+ node.query("DROP SETTINGS PROFILE default", exitcode=exitcode, message=message)
+
+ with Scenario("I drop multiple settings profiles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Drop("1.0")]):
+ with cleanup("profile3"), cleanup("profile4"):
+ with When("I drop multiple settings profiles"):
+ node.query("DROP SETTINGS PROFILE profile3, profile4")
+
+ with Scenario("I drop settings profile on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Drop_OnCluster("1.0")]):
+ try:
+ with Given("I have a settings profile"):
+ node.query("CREATE SETTINGS PROFILE profile5 ON CLUSTER sharded_cluster")
+ with When("I run drop settings profile command"):
+ node.query("DROP SETTINGS PROFILE profile5 ON CLUSTER sharded_cluster")
+ finally:
+ with Finally("I drop the profile in case it still exists"):
+ node.query("DROP SETTINGS PROFILE IF EXISTS profile5 ON CLUSTER sharded_cluster")
+
+ with Scenario("I drop settings profile on fake cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_Drop_OnCluster("1.0")]):
+ with When("I run drop settings profile command"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("DROP SETTINGS PROFILE profile6 ON CLUSTER fake_cluster", exitcode=exitcode, message=message)
diff --git a/tests/testflows/rbac/tests/syntax/drop_user.py b/tests/testflows/rbac/tests/syntax/drop_user.py
new file mode 100644
index 00000000000..c5a2d16d7e1
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/drop_user.py
@@ -0,0 +1,98 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("drop user")
+def feature(self, node="clickhouse1"):
+ """Check drop user query syntax.
+
+ ```sql
+ DROP USER [IF EXISTS] name [,...] [ON CLUSTER cluster_name]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def setup(user):
+ try:
+ with Given("I have a user"):
+ node.query(f"CREATE USER {user}")
+ yield
+ finally:
+ with Finally("I drop the user"):
+ node.query(f"DROP USER IF EXISTS {user}")
+
+ def cleanup_user(user):
+ with Given(f"I ensure that user {user} does not exist"):
+ node.query(f"DROP USER IF EXISTS {user}")
+
+ with Scenario("I drop user with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Drop("1.0")]):
+ with setup("user0"):
+ with When("I drop user"):
+ node.query("DROP USER user0")
+
+ with Scenario("I drop user, does not exist, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Drop("1.0")]):
+ user = "user0"
+ cleanup_user(user)
+ with When(f"I drop user {user}"):
+ exitcode, message = errors.user_not_found_in_disk(name=user)
+ node.query(f"DROP USER {user}", exitcode=exitcode, message=message)
+ del user
+
+ with Scenario("I drop multiple users", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Drop("1.0")]):
+ with setup("user1"), setup("user2"):
+ with When("I drop multiple users"):
+ node.query("DROP USER user1, user2")
+
+ with Scenario("I drop user if exists, user does exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Drop_IfExists("1.0")]):
+ with setup("user3"):
+ with When("I drop user that exists"):
+ node.query("DROP USER IF EXISTS user3")
+
+ with Scenario("I drop user if exists, user does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Drop_IfExists("1.0")]):
+ cleanup_user("user3")
+ with When("I drop nonexistant user"):
+ node.query("DROP USER IF EXISTS user3")
+
+ with Scenario("I drop default user, throws error", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Drop("1.0")]):
+ with When("I drop user"):
+ exitcode, message = errors.cannot_remove_user_default()
+ node.query("DROP USER default", exitcode=exitcode, message=message)
+
+ with Scenario("I drop multiple users where one does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Drop_IfExists("1.0")]):
+ with setup("user3"):
+ with When("I drop multiple users where one does not exist"):
+ node.query("DROP USER IF EXISTS user3, user4")
+
+ with Scenario("I drop multiple users where both do not exist", requirements=[
+ RQ_SRS_006_RBAC_User_Drop_IfExists("1.0")]):
+ with When("I drop the nonexistant users"):
+ node.query("DROP USER IF EXISTS user5, user6")
+
+ with Scenario("I drop user from specific cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Drop_OnCluster("1.0")]):
+ try:
+ with Given("I have a user on cluster"):
+ node.query("CREATE USER user4 ON CLUSTER sharded_cluster")
+ with When("I drop a user from the cluster"):
+ node.query("DROP USER user4 ON CLUSTER sharded_cluster")
+ finally:
+ with Finally("I make sure the user is dropped"):
+ node.query("DROP USER IF EXISTS user4 ON CLUSTER sharded_cluster")
+
+ with Scenario("I drop user from fake cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_Drop_OnCluster("1.0")]):
+ with When("I drop a user from the fake cluster"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("DROP USER user5 ON CLUSTER fake_cluster", exitcode=exitcode, message=message)
diff --git a/tests/testflows/rbac/tests/syntax/feature.py b/tests/testflows/rbac/tests/syntax/feature.py
new file mode 100644
index 00000000000..aac786ff85c
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/feature.py
@@ -0,0 +1,34 @@
+from testflows.core import *
+
+@TestFeature
+@Name("syntax")
+def feature(self):
+ Feature(run=load("rbac.tests.syntax.create_user", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.alter_user", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.drop_user", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.show_create_user", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.create_role", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.alter_role", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.drop_role", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.show_create_role", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.grant_role", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.grant_privilege","feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.show_grants", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.revoke_role", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.revoke_privilege","feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.create_row_policy", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.alter_row_policy", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.drop_row_policy", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.show_create_row_policy", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.show_row_policies", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.create_quota", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.alter_quota", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.drop_quota", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.show_create_quota", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.show_quotas", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.create_settings_profile", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.alter_settings_profile", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.drop_settings_profile", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.show_create_settings_profile", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.set_default_role", "feature"), flags=TE)
+ Feature(run=load("rbac.tests.syntax.set_role","feature"), flags=TE)
\ No newline at end of file
diff --git a/tests/testflows/rbac/tests/syntax/grant_privilege.py b/tests/testflows/rbac/tests/syntax/grant_privilege.py
new file mode 100644
index 00000000000..fdb3224de8e
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/grant_privilege.py
@@ -0,0 +1,134 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@contextmanager
+def setup(node):
+ try:
+ with Given("I have some users and roles"):
+ node.query("CREATE USER OR REPLACE user0 ON CLUSTER sharded_cluster")
+ node.query("CREATE USER OR REPLACE user1")
+ node.query("CREATE ROLE OR REPLACE role1")
+ yield
+ finally:
+ with Finally("I drop the users and roles"):
+ node.query("DROP USER IF EXISTS user0 ON CLUSTER sharded_cluster")
+ node.query("DROP USER IF EXISTS user1")
+ node.query("DROP ROLE IF EXISTS role1")
+
+
+@TestOutline(Scenario)
+@Examples("privilege on allow_introspection", [
+ ("dictGet", ("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_DictGet("1.0"))),
+ ("INTROSPECTION", ("*.*",), True, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_Introspection("1.0"))),
+ ("SELECT", ("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_Select("1.0"))),
+ ("INSERT",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_Insert("1.0"))),
+ ("ALTER",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_Alter("1.0"))),
+ ("CREATE",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_Create("1.0"))),
+ ("DROP",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_Drop("1.0"))),
+ ("TRUNCATE",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_Truncate("1.0"))),
+ ("OPTIMIZE",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_Optimize("1.0"))),
+ ("SHOW",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_Show("1.0"))),
+ ("KILL QUERY",("*.*",), False, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_KillQuery("1.0"))),
+ ("ACCESS MANAGEMENT",("*.*",), False, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_AccessManagement("1.0"))),
+ ("SYSTEM",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_System("1.0"))),
+ ("SOURCES",("*.*",), False, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_Sources("1.0"))),
+ ("ALL",("*.*",), True, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_All("1.0"))),
+ ("ALL PRIVILEGES",("*.*",), True, Requirements(RQ_SRS_006_RBAC_Grant_Privilege_All("1.0"))), #alias for all
+ ],)
+def grant_privileges(self, privilege, on, allow_introspection, node="clickhouse1"):
+ grant_privilege(privilege=privilege, on=on, allow_introspection=allow_introspection, node=node)
+
+@TestOutline(Scenario)
+@Requirements(RQ_SRS_006_RBAC_Grant_Privilege_GrantOption("1.0"))
+def grant_privilege(self, privilege, on, allow_introspection, node="clickhouse1"):
+ node = self.context.cluster.node(node)
+
+ for on_ in on:
+ with When(f"I grant {privilege} privilege to user on {on_}"):
+ with setup(node):
+ settings = []
+ if allow_introspection:
+ settings.append(("allow_introspection_functions", 1))
+ node.query("SET allow_introspection_functions = 1")
+ with When("I grant privilege without grant option"):
+ node.query(f"GRANT {privilege} ON {on_} TO user0", settings=settings)
+ with When("I grant privilege with grant option"):
+ node.query(f"GRANT {privilege} ON {on_} TO user1 WITH GRANT OPTION", settings=settings)
+
+ #grant column specific for some column 'x'
+ with When("I grant privilege with columns"):
+ node.query(f"GRANT {privilege}(x) ON {on_} TO user0", settings=settings)
+
+@TestFeature
+@Name("grant privilege")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check grant privilege syntax.
+
+ ```sql
+ GRANT [ON CLUSTER cluster_name]
+ privilege {SELECT | SELECT(columns) | INSERT | ALTER | CREATE | DROP | TRUNCATE | OPTIMIZE | SHOW | KILL QUERY | ACCESS MANAGEMENT | SYSTEM | INTROSPECTION | SOURCES | dictGet | NONE |ALL [PRIVILEGES]} [, ...]
+ ON {*.* | database.* | database.table | * | table}
+ TO {user | role | CURRENT_USER} [,...]
+ [WITH GRANT OPTION]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ Scenario(run=grant_privileges)
+
+ # with nonexistant object name, GRANT assumes type role
+ with Scenario("I grant privilege to role that does not exist", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Privilege_None("1.0")]):
+ with Given("I ensure that role does not exist"):
+ node.query("DROP ROLE IF EXISTS role0")
+ with When("I grant privilege ON CLUSTER"):
+ exitcode, message = errors.role_not_found_in_disk(name="role0")
+ node.query("GRANT NONE TO role0", exitcode=exitcode, message=message)
+
+ with Scenario("I grant privilege ON CLUSTER", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Privilege_OnCluster("1.0"),
+ RQ_SRS_006_RBAC_Grant_Privilege_None("1.0")]):
+ with setup(node):
+ with When("I grant privilege ON CLUSTER"):
+ node.query("GRANT ON CLUSTER sharded_cluster NONE TO user0")
+
+ with Scenario("I grant privilege on fake cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Privilege_OnCluster("1.0")]):
+ with setup(node):
+ with When("I grant privilege ON CLUSTER"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("GRANT ON CLUSTER fake_cluster NONE TO user0", exitcode=exitcode, message=message)
+
+ with Scenario("I grant privilege to multiple users and roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Privilege_To("1.0"),
+ RQ_SRS_006_RBAC_Grant_Privilege_None("1.0")]):
+ with setup(node):
+ with When("I grant privilege to several users"):
+ node.query("GRANT NONE TO user0, user1, role1")
+
+ with Scenario("I grant privilege to current user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Privilege_ToCurrentUser("1.0"),
+ RQ_SRS_006_RBAC_Grant_Privilege_None("1.0")]):
+ with setup(node):
+ with When("I grant privilege to current user"):
+ node.query("GRANT NONE TO CURRENT_USER", settings = [("user","user0")])
+
+ with Scenario("I grant privilege NONE to default user, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Privilege_ToCurrentUser("1.0"),
+ RQ_SRS_006_RBAC_Grant_Privilege_None("1.0")]):
+ with setup(node):
+ with When("I grant privilege to current user"):
+ exitcode, message = errors.cannot_update_default()
+ node.query("GRANT NONE TO CURRENT_USER", exitcode=exitcode, message=message)
+
+ with Scenario("I grant privilege with grant option", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Privilege_GrantOption("1.0"),
+ RQ_SRS_006_RBAC_Grant_Privilege_None("1.0")]):
+ with setup(node):
+ with When("I grant privilege with grant option"):
+ node.query("GRANT NONE ON *.* TO user0 WITH GRANT OPTION")
diff --git a/tests/testflows/rbac/tests/syntax/grant_role.py b/tests/testflows/rbac/tests/syntax/grant_role.py
new file mode 100644
index 00000000000..48e5101e6cf
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/grant_role.py
@@ -0,0 +1,115 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("grant role")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check grant query syntax.
+
+ ```sql
+ GRANT ON CLUSTER [cluster_name] role [,...] TO {user | another_role | CURRENT_USER} [,...] [WITH ADMIN OPTION]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def setup(users=0,roles=0):
+ try:
+ with Given("I have some users and roles"):
+ for i in range(users):
+ node.query(f"CREATE USER OR REPLACE user{i}")
+ for j in range(roles):
+ node.query(f"CREATE ROLE OR REPLACE role{j}")
+ yield
+ finally:
+ with Finally("I drop the users and roles"):
+ for i in range(users):
+ node.query(f"DROP USER IF EXISTS user{i}")
+ for j in range(roles):
+ node.query(f"DROP ROLE IF EXISTS role{j}")
+
+ with Scenario("I grant a role to a user",flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Role("1.0")]):
+ with setup(1,1):
+ with When("I grant a role"):
+ node.query("GRANT role0 TO user0")
+
+ with Scenario("I grant a nonexistent role to user", requirements=[
+ RQ_SRS_006_RBAC_Grant_Role("1.0")]):
+ with setup(1,0):
+ with When("I grant nonexistent role to a user"):
+ exitcode, message = errors.role_not_found_in_disk(name="role0")
+ node.query("GRANT role0 TO user0", exitcode=exitcode, message=message)
+
+ # with nonexistent object name, GRANT assumes type role (treats user0 as role)
+ with Scenario("I grant a role to a nonexistent user", requirements=[
+ RQ_SRS_006_RBAC_Grant_Role("1.0")]):
+ with setup(0,1):
+ with When("I grant role to a nonexistent user"):
+ exitcode, message = errors.role_not_found_in_disk(name="user0")
+ node.query("GRANT role0 TO user0", exitcode=exitcode, message=message)
+
+ with Scenario("I grant a nonexistent role to a nonexistent user", requirements=[
+ RQ_SRS_006_RBAC_Grant_Role("1.0")]):
+ with setup(0,0):
+ with When("I grant nonexistent role to a nonexistent user"):
+ exitcode, message = errors.role_not_found_in_disk(name="role0")
+ node.query("GRANT role0 TO user0", exitcode=exitcode, message=message)
+
+ with Scenario("I grant a role to multiple users", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Role("1.0")]):
+ with setup(2,1):
+ with When("I grant role to a multiple users"):
+ node.query("GRANT role0 TO user0, user1")
+
+ with Scenario("I grant multiple roles to multiple users", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Role("1.0")]):
+ with setup(2,2):
+ with When("I grant multiple roles to multiple users"):
+ node.query("GRANT role0, role1 TO user0, user1")
+
+ with Scenario("I grant role to current user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Role_CurrentUser("1.0")]):
+ with setup(1,1):
+ with Given("I have a user with access management privilege"):
+ node.query("GRANT ACCESS MANAGEMENT ON *.* TO user0")
+ with When("I grant role to current user"):
+ node.query("GRANT role0 TO CURRENT_USER", settings = [("user","user0")])
+
+ with Scenario("I grant role to default user, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Role_CurrentUser("1.0")]):
+ with setup(1,1):
+ with When("I grant role to default user"):
+ exitcode, message = errors.cannot_update_default()
+ node.query("GRANT role0 TO CURRENT_USER", exitcode=exitcode, message=message)
+
+ with Scenario("I grant role to user with admin option", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Role_AdminOption("1.0")]):
+ with setup(1,1):
+ with When("I grant role to a user with admin option"):
+ node.query("GRANT role0 TO user0 WITH ADMIN OPTION")
+
+ with Scenario("I grant role to user on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Role_OnCluster("1.0")]):
+ try:
+ with Given("I have a user and a role on a cluster"):
+ node.query("CREATE USER user0 ON CLUSTER sharded_cluster")
+ node.query("CREATE ROLE role0 ON CLUSTER sharded_cluster")
+ with When("I grant the role to the user"):
+ node.query("GRANT ON CLUSTER sharded_cluster role0 TO user0")
+ finally:
+ with Finally("I drop the user and role"):
+ node.query("DROP USER user0 ON CLUSTER sharded_cluster")
+ node.query("DROP ROLE role0 ON CLUSTER sharded_cluster")
+
+ with Scenario("I grant role to user on fake cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Grant_Role_OnCluster("1.0")]):
+ with setup(1,1):
+ with When("I grant the role to the user"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("GRANT ON CLUSTER fake_cluster role0 TO user0", exitcode=exitcode, message=message)
\ No newline at end of file
diff --git a/tests/testflows/rbac/tests/syntax/revoke_privilege.py b/tests/testflows/rbac/tests/syntax/revoke_privilege.py
new file mode 100644
index 00000000000..62130c4bf9a
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/revoke_privilege.py
@@ -0,0 +1,159 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@contextmanager
+def setup(node):
+ try:
+ with Given("I have some users and roles"):
+ node.query("CREATE USER OR REPLACE user0 ON CLUSTER sharded_cluster")
+ node.query("CREATE USER OR REPLACE user1")
+ node.query("CREATE ROLE OR REPLACE role1")
+ yield
+ finally:
+ with Finally("I drop the users and roles"):
+ node.query("DROP USER IF EXISTS user0 ON CLUSTER sharded_cluster")
+ node.query("DROP USER IF EXISTS user1")
+ node.query("DROP ROLE IF EXISTS role1")
+
+
+@TestOutline(Scenario)
+@Examples("privilege on allow_introspection", [
+ ("dictGet", ("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_DictGet("1.0"))),
+ ("INTROSPECTION", ("*.*",), True, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_Introspection("1.0"))),
+ ("SELECT", ("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_Select("1.0"))),
+ ("INSERT",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_Insert("1.0"))),
+ ("ALTER",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_Alter("1.0"))),
+ ("CREATE",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_Create("1.0"))),
+ ("DROP",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_Drop("1.0"))),
+ ("TRUNCATE",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_Truncate("1.0"))),
+ ("OPTIMIZE",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_Optimize("1.0"))),
+ ("SHOW",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_Show("1.0"))),
+ ("KILL QUERY",("*.*",), False, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_KillQuery("1.0"))),
+ ("ACCESS MANAGEMENT",("*.*",), False, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_AccessManagement("1.0"))),
+ ("SYSTEM",("db0.table0","db0.*","*.*","tb0","*"), False, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_System("1.0"))),
+ ("SOURCES",("*.*",), False, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_Sources("1.0"))),
+ ("ALL",("*.*",), True, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_All("1.0"))),
+ ("ALL PRIVILEGES",("*.*",), True, Requirements(RQ_SRS_006_RBAC_Revoke_Privilege_All("1.0"))), #alias for all
+ ],)
+def revoke_privileges(self, privilege, on, allow_introspection, node="clickhouse1"):
+ revoke_privilege(privilege=privilege, on=on, allow_introspection=allow_introspection, node=node)
+
+@TestOutline(Scenario)
+@Requirements([RQ_SRS_006_RBAC_Revoke_Privilege_Any("1.0") , RQ_SRS_006_RBAC_Revoke_Privilege_PrivelegeColumns("1.0")])
+def revoke_privilege(self, privilege, on, allow_introspection, node="clickhouse1"):
+ node = self.context.cluster.node(node)
+ for on_ in on:
+ with When(f"I revoke {privilege} privilege from user on {on_}"):
+ with setup(node):
+ settings = []
+ if allow_introspection:
+ settings.append(("allow_introspection_functions", 1))
+ node.query("SET allow_introspection_functions = 1")
+ with When("I revoke privilege without columns"):
+ node.query(f"REVOKE {privilege} ON {on_} FROM user0", settings=settings)
+
+ #revoke column specific for some column 'x'
+ with When("I revoke privilege with columns"):
+ node.query(f"REVOKE {privilege}(x) ON {on_} FROM user0", settings=settings)
+
+@TestFeature
+@Name("revoke privilege")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check revoke privilege syntax.
+
+ ```sql
+ REVOKE [ON CLUSTER cluster_name] privilege
+ [(column_name [,...])] [,...]
+ ON {db.table|db.*|*.*|table|*}
+ FROM {user | CURRENT_USER} [,...] | ALL | ALL EXCEPT {user | CURRENT_USER} [,...]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ Scenario(run=revoke_privileges)
+
+ with Scenario("I revoke privilege ON CLUSTER", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Privilege_Cluster("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Privilege_None("1.0")]):
+ with setup(node):
+ with When("I revoke privilege ON CLUSTER"):
+ node.query("REVOKE ON CLUSTER sharded_cluster NONE FROM user0")
+
+ with Scenario("I revoke privilege ON fake CLUSTER, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Privilege_Cluster("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Privilege_None("1.0")]):
+ with setup(node):
+ with When("I revoke privilege ON CLUSTER"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("REVOKE ON CLUSTER fake_cluster NONE FROM user0",
+ exitcode=exitcode, message=message)
+
+ with Scenario("I revoke privilege from multiple users and roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Privilege_From("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Privilege_None("1.0")]):
+ with setup(node):
+ with When("I revoke privilege from multiple users"):
+ node.query("REVOKE NONE FROM user0, user1, role1")
+
+ with Scenario("I revoke privilege from current user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Privilege_From("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Privilege_None("1.0")]):
+ with setup(node):
+ with When("I revoke privilege from current user"):
+ node.query("REVOKE NONE FROM CURRENT_USER", settings = [("user","user0")])
+
+ with Scenario("I revoke privilege from all users", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Privilege_From("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Privilege_None("1.0")]):
+ with setup(node):
+ with When("I revoke privilege from all users"):
+ exitcode, message = errors.cannot_update_default()
+ node.query("REVOKE NONE FROM ALL", exitcode=exitcode,message=message)
+
+ with Scenario("I revoke privilege from default user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Privilege_From("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Privilege_None("1.0")]):
+ with setup(node):
+ with When("I revoke privilege from default user"):
+ exitcode, message = errors.cannot_update_default()
+ node.query("REVOKE NONE FROM default", exitcode=exitcode,message=message)
+
+ #By default, ClickHouse treats unnamed object as role
+ with Scenario("I revoke privilege from nonexistent role, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Privilege_From("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Privilege_None("1.0")]):
+ role = "role5"
+ with Given(f"I ensure that role {role} does not exist"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with When(f"I revoke privilege from nonexistent role {role}"):
+ exitcode, message = errors.role_not_found_in_disk(role)
+ node.query(f"REVOKE NONE FROM {role}", exitcode=exitcode,message=message)
+
+ with Scenario("I revoke privilege from ALL EXCEPT nonexistent role, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Privilege_From("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Privilege_None("1.0")]):
+ role = "role5"
+ with Given(f"I ensure that role {role} does not exist"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+ with When(f"I revoke privilege from nonexistent role {role}"):
+ exitcode, message = errors.role_not_found_in_disk(role)
+ node.query(f"REVOKE NONE FROM ALL EXCEPT {role}", exitcode=exitcode,message=message)
+
+ with Scenario("I revoke privilege from all except some users and roles", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Privilege_From("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Privilege_None("1.0")]):
+ with setup(node):
+ with When("I revoke privilege all except some users"):
+ node.query("REVOKE NONE FROM ALL EXCEPT default, user0, role1")
+
+ with Scenario("I revoke privilege from all except current user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Privilege_From("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Privilege_None("1.0")]):
+ with setup(node):
+ with When("I revoke privilege from all except current user"):
+ node.query("REVOKE NONE FROM ALL EXCEPT CURRENT_USER")
\ No newline at end of file
diff --git a/tests/testflows/rbac/tests/syntax/revoke_role.py b/tests/testflows/rbac/tests/syntax/revoke_role.py
new file mode 100644
index 00000000000..8b150c74705
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/revoke_role.py
@@ -0,0 +1,198 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("revoke role")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check revoke query syntax.
+
+ ```sql
+ REVOKE [ON CLUSTER cluster_name] [ADMIN OPTION FOR]
+ role [,...] FROM {user | role | CURRENT_USER} [,...]
+ | ALL | ALL EXCEPT {user_name | role_name | CURRENT_USER} [,...]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def setup(users=2,roles=2):
+ try:
+ with Given("I have some users"):
+ for i in range(users):
+ node.query(f"CREATE USER OR REPLACE user{i}")
+ with And("I have some roles"):
+ for i in range(roles):
+ node.query(f"CREATE ROLE OR REPLACE role{i}")
+ yield
+ finally:
+ with Finally("I drop the users"):
+ for i in range(users):
+ node.query(f"DROP USER IF EXISTS user{i}")
+ with And("I drop the roles"):
+ for i in range(roles):
+ node.query(f"DROP ROLE IF EXISTS role{i}")
+
+ with Scenario("I revoke a role from a user",flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0")]):
+ with setup():
+ with When("I revoke a role"):
+ node.query("REVOKE role0 FROM user0")
+
+ with Scenario("I revoke a nonexistent role from user", requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0")]):
+ with setup(1,0):
+ with When("I revoke nonexistent role from a user"):
+ exitcode, message = errors.role_not_found_in_disk(name="role0")
+ node.query("REVOKE role0 FROM user0", exitcode=exitcode, message=message)
+
+ # with nonexistent object name, REVOKE assumes type role (treats user0 as role)
+ with Scenario("I revoke a role from a nonexistent user", requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0")]):
+ with setup(0,1):
+ with When("I revoke role from a nonexistent user"):
+ exitcode, message = errors.role_not_found_in_disk(name="user0")
+ node.query("REVOKE role0 FROM user0", exitcode=exitcode, message=message)
+
+ # with nonexistent object name, REVOKE assumes type role (treats user0 as role)
+ with Scenario("I revoke a role from ALL EXCEPT nonexistent user", requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0")]):
+ with setup(0,1):
+ with When("I revoke role from a nonexistent user"):
+ exitcode, message = errors.role_not_found_in_disk(name="user0")
+ node.query("REVOKE role0 FROM ALL EXCEPT user0", exitcode=exitcode, message=message)
+
+ with Scenario("I revoke a nonexistent role from a nonexistent user", requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0")]):
+ with setup(0,0):
+ with When("I revoke nonexistent role from a nonexistent user"):
+ exitcode, message = errors.role_not_found_in_disk(name="role0")
+ node.query("REVOKE role0 FROM user0", exitcode=exitcode, message=message)
+
+ with Scenario("I revoke a role from multiple users", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0")]):
+ with setup():
+ with When("I revoke a role from multiple users"):
+ node.query("REVOKE role0 FROM user0, user1")
+
+ with Scenario("I revoke multiple roles from multiple users", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0")]):
+ with setup():
+ node.query("REVOKE role0, role1 FROM user0, user1")
+
+ #user is default, expect exception
+ with Scenario("I revoke a role from default user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0")]):
+ with setup():
+ with When("I revoke a role from default user"):
+ exitcode, message = errors.cannot_update_default()
+ node.query("REVOKE role0 FROM CURRENT_USER", exitcode=exitcode, message=message)
+
+ #user is user0
+ with Scenario("I revoke a role from current user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0")]):
+ with setup():
+ with When("I revoke a role from current user"):
+ node.query("REVOKE role0 FROM CURRENT_USER", settings = [("user","user0")])
+
+ #user is default, expect exception
+ with Scenario("I revoke a role from all", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0")]):
+ with setup():
+ with When("I revoke a role from all"):
+ exitcode, message = errors.cannot_update_default()
+ node.query("REVOKE role0 FROM ALL", exitcode=exitcode, message=message)
+
+ #user is default, expect exception
+ with Scenario("I revoke multiple roles from all", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0")]):
+ with setup():
+ with When("I revoke multiple roles from all"):
+ exitcode, message = errors.cannot_update_default()
+ node.query("REVOKE role0, role1 FROM ALL", exitcode=exitcode, message=message)
+
+ with Scenario("I revoke a role from all but current user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0")]):
+ with setup():
+ with When("I revoke a role from all except current"):
+ node.query("REVOKE role0 FROM ALL EXCEPT CURRENT_USER")
+
+ with Scenario("I revoke a role from all but default user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0")]):
+ with setup():
+ with When("I revoke a role from all except default"):
+ node.query("REVOKE role0 FROM ALL EXCEPT default",
+ settings = [("user","user0")])
+
+ with Scenario("I revoke multiple roles from all but default user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Role_Keywords("1.0")]):
+ with setup():
+ with When("I revoke multiple roles from all except default"):
+ node.query("REVOKE role0, role1 FROM ALL EXCEPT default", settings = [("user","user0")])
+
+ with Scenario("I revoke a role from a role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0")]):
+ with setup():
+ with When("I revoke a role from a role"):
+ node.query("REVOKE role0 FROM role1")
+
+ with Scenario("I revoke a role from a role and a user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0")]):
+ with setup():
+ with When("I revoke a role from multiple roles"):
+ node.query("REVOKE role0 FROM role1, user0")
+
+ with Scenario("I revoke a role from a user on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role_Cluster("1.0")]):
+ with Given("I have a role and a user on a cluster"):
+ node.query("CREATE USER OR REPLACE user0 ON CLUSTER sharded_cluster")
+ node.query("CREATE ROLE OR REPLACE role0 ON CLUSTER sharded_cluster")
+ with When("I revoke a role from user on a cluster"):
+ node.query("REVOKE ON CLUSTER sharded_cluster role0 FROM user0")
+ with Finally("I drop the user and role"):
+ node.query("DROP USER IF EXISTS user0 ON CLUSTER sharded_cluster")
+ node.query("DROP ROLE IF EXISTS role0 ON CLUSTER sharded_cluster")
+
+ with Scenario("I revoke a role on fake cluster, throws exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role_Cluster("1.0")]):
+ with When("I revoke a role from user on a cluster"):
+ exitcode, message = errors.cluster_not_found("fake_cluster")
+ node.query("REVOKE ON CLUSTER fake_cluster role0 FROM user0", exitcode=exitcode, message=message)
+
+ with Scenario("I revoke multiple roles from multiple users on cluster", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0"),
+ RQ_SRS_006_RBAC_Revoke_Role_Cluster("1.0")]):
+ with Given("I have multiple roles and multiple users on a cluster"):
+ for i in range(2):
+ node.query(f"CREATE USER OR REPLACE user{i} ON CLUSTER sharded_cluster")
+ node.query(f"CREATE ROLE OR REPLACE role{i} ON CLUSTER sharded_cluster")
+ with When("I revoke multiple roles from multiple users on cluster"):
+ node.query("REVOKE ON CLUSTER sharded_cluster role0, role1 FROM user0, user1")
+ with Finally("I drop the roles and users"):
+ for i in range(2):
+ node.query(f"DROP USER IF EXISTS user{i} ON CLUSTER sharded_cluster")
+ node.query(f"DROP ROLE IF EXISTS role{i} ON CLUSTER sharded_cluster")
+
+ with Scenario("I revoke admin option for role from a user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_AdminOption("1.0")]):
+ with setup():
+ with When("I revoke admin option for role from a user"):
+ node.query("REVOKE ADMIN OPTION FOR role0 FROM user0")
+
+ with Scenario("I revoke admin option for multiple roles from multiple users", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Revoke_Role("1.0"),
+ RQ_SRS_006_RBAC_Revoke_AdminOption("1.0")]):
+ with setup():
+ with When("I revoke admin option for multiple roles from multiple users"):
+ node.query("REVOKE ADMIN OPTION FOR role0, role1 FROM user0, user1")
\ No newline at end of file
diff --git a/tests/testflows/rbac/tests/syntax/set_default_role.py b/tests/testflows/rbac/tests/syntax/set_default_role.py
new file mode 100644
index 00000000000..ec0d41554da
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/set_default_role.py
@@ -0,0 +1,120 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("set default role")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check set default role query syntax.
+
+ ```sql
+ SET DEFAULT ROLE {NONE | role [,...] | ALL | ALL EXCEPT role [,...]} TO {user|CURRENT_USER} [,...]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def setup(users=2,roles=2):
+ try:
+ with Given("I have some users"):
+ for i in range(users):
+ node.query(f"CREATE USER OR REPLACE user{i}")
+ with And("I have some roles"):
+ for i in range(roles):
+ node.query(f"CREATE ROLE OR REPLACE role{i}")
+ yield
+ finally:
+ with Finally("I drop the users"):
+ for i in range(users):
+ node.query(f"DROP USER IF EXISTS user{i}")
+ with And("I drop the roles"):
+ for i in range(roles):
+ node.query(f"DROP ROLE IF EXISTS role{i}")
+
+ with Scenario("I set default a nonexistent role to user", requirements=[
+ RQ_SRS_006_RBAC_SetDefaultRole("1.0")]):
+ with setup(1,0):
+ with When("I set default nonexistent role to a user"):
+ exitcode, message = errors.role_not_found_in_disk(name="role0")
+ node.query("SET DEFAULT ROLE role0 TO user0", exitcode=exitcode, message=message)
+
+ with Scenario("I set default ALL EXCEPT a nonexistent role to user", requirements=[
+ RQ_SRS_006_RBAC_SetDefaultRole("1.0")]):
+ with setup(1,0):
+ with When("I set default nonexistent role to a user"):
+ exitcode, message = errors.role_not_found_in_disk(name="role0")
+ node.query("SET DEFAULT ROLE ALL EXCEPT role0 TO user0", exitcode=exitcode, message=message)
+
+ with Scenario("I set default a role to a nonexistent user", requirements=[
+ RQ_SRS_006_RBAC_SetDefaultRole("1.0")]):
+ with setup(0,1):
+ with When("I set default role to a nonexistent user"):
+ exitcode, message = errors.user_not_found_in_disk(name="user0")
+ node.query("SET DEFAULT ROLE role0 TO user0", exitcode=exitcode, message=message)
+
+ #in SET DEFAULT ROLE, the nonexistent user is noticed first and becomes the thrown exception
+ with Scenario("I set default a nonexistent role to a nonexistent user", requirements=[
+ RQ_SRS_006_RBAC_SetDefaultRole("1.0")]):
+ with setup(0,0):
+ with When("I set default nonexistent role to a nonexistent user"):
+ exitcode, message = errors.user_not_found_in_disk(name="user0")
+ node.query("SET DEFAULT ROLE role0 TO user0", exitcode=exitcode, message=message)
+
+ try:
+ with Given("I have some roles and some users"):
+ for i in range(2):
+ node.query(f"CREATE ROLE role{i}")
+ node.query(f"CREATE USER user{i}")
+ node.query(f"GRANT role0, role1 TO user0, user1")
+
+ with Scenario("I set default role for a user to none", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetDefaultRole_None("1.0")]):
+ with When("I set no roles default for user"):
+ node.query("SET DEFAULT ROLE NONE TO user0")
+
+ with Scenario("I set one default role for a user", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetDefaultRole("1.0")]):
+ with When("I set a default role for user "):
+ node.query("SET DEFAULT ROLE role0 TO user0")
+
+ with Scenario("I set one default role for user default, throws exception", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetDefaultRole("1.0")]):
+ with When("I set a default role for default"):
+ exitcode, message = errors.cannot_update_default()
+ node.query("SET DEFAULT ROLE role0 TO default", exitcode=exitcode, message=message)
+
+ with Scenario("I set multiple default roles for a user", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetDefaultRole("1.0")]):
+ with When("I set multiple default roles to user"):
+ node.query("SET DEFAULT ROLE role0, role1 TO user0")
+
+ with Scenario("I set multiple default roles for multiple users", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetDefaultRole("1.0")]):
+ with When("I set multiple default roles to multiple users"):
+ node.query("SET DEFAULT ROLE role0, role1 TO user0, user1")
+
+ with Scenario("I set all roles as default for a user", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetDefaultRole_All("1.0")]):
+ with When("I set all roles default to user"):
+ node.query("SET DEFAULT ROLE ALL TO user0")
+
+ with Scenario("I set all roles except one for a user", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetDefaultRole_AllExcept("1.0")]):
+ with When("I set all except one role default to user"):
+ node.query("SET DEFAULT ROLE ALL EXCEPT role0 TO user0")
+
+ with Scenario("I set default role for current user", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetDefaultRole_CurrentUser("1.0")]):
+ with When("I set default role to current user"):
+ node.query("GRANT ACCESS MANAGEMENT ON *.* TO user0")
+ node.query("SET DEFAULT ROLE role0 TO CURRENT_USER", settings = [("user","user0")])
+
+ finally:
+ with Finally("I drop the roles and users"):
+ for i in range(2):
+ node.query(f"DROP ROLE IF EXISTS role{i}")
+ node.query(f"DROP USER IF EXISTS user{i}")
diff --git a/tests/testflows/rbac/tests/syntax/set_role.py b/tests/testflows/rbac/tests/syntax/set_role.py
new file mode 100644
index 00000000000..fdd1c95112d
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/set_role.py
@@ -0,0 +1,91 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("set role")
+@Args(format_description=False)
+def feature(self, node="clickhouse1"):
+ """Check set role query syntax.
+
+ ```
+ SET ROLE {DEFAULT | NONE | role [,...] | ALL | ALL EXCEPT role [,...]}
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def setup(roles=0):
+ try:
+ with Given("I have some roles"):
+ for i in range(roles):
+ node.query(f"CREATE ROLE role{i}")
+ yield
+ finally:
+ with Finally("I drop the roles"):
+ for i in range(roles):
+ node.query(f"DROP ROLE IF EXISTS role{i}")
+
+ with Scenario("I set default role for current user", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetRole_Default("1.0")]):
+ with When("I set default role for current user"):
+ node.query("SET ROLE DEFAULT")
+
+ with Scenario("I set no role for current user", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetRole_None("1.0")]):
+ with When("I set no role for current user"):
+ node.query("SET ROLE NONE")
+
+ with Scenario("I set nonexistent role, throws exception", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetRole_None("1.0")]):
+ with Given("I ensure that role role5 does not exist"):
+ node.query("DROP ROLE IF EXISTS role5")
+ with When("I set nonexistent role for current user"):
+ exitcode, message = errors.role_not_found_in_disk("role5")
+ node.query("SET ROLE role5", exitcode=exitcode, message=message)
+
+ with Scenario("I set nonexistent role, throws exception", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetRole_None("1.0")]):
+ with Given("I ensure that role role5 does not exist"):
+ node.query("DROP ROLE IF EXISTS role5")
+ with When("I set nonexistent role for current user"):
+ exitcode, message = errors.role_not_found_in_disk("role5")
+ node.query("SET ROLE ALL EXCEPT role5", exitcode=exitcode, message=message)
+
+ with Scenario("I set one role for current user", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetRole("1.0")]):
+ with setup(1):
+ with Given("I have a user"):
+ node.query("CREATE USER OR REPLACE user0")
+ with And("I grant user a role"):
+ node.query("GRANT role0 TO user0")
+ with When("I set role for the user"):
+ node.query("SET ROLE role0", settings = [("user","user0")])
+ with Finally("I drop the user"):
+ node.query("DROP USER user0")
+
+ with Scenario("I set multiple roles for current user", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetRole("1.0")]):
+ with setup(2):
+ with Given("I have a user"):
+ node.query("CREATE USER OR REPLACE user0")
+ with And("I grant user a role"):
+ node.query("GRANT role0, role1 TO user0")
+ with When("I set roles for the user"):
+ node.query("SET ROLE role0, role1", settings = [("user","user0")])
+ with Finally("I drop the user"):
+ node.query("DROP USER user0")
+
+ with Scenario("I set all roles for current user", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetRole_All("1.0")]):
+ with When("I set all roles for current user"):
+ node.query("SET ROLE ALL")
+
+ with Scenario("I set all roles except one for current user", flags = TE, requirements=[
+ RQ_SRS_006_RBAC_SetRole_AllExcept("1.0")]):
+ with setup(1):
+ with When("I run set role command"):
+ node.query("SET ROLE ALL EXCEPT role0")
\ No newline at end of file
diff --git a/tests/testflows/rbac/tests/syntax/show_create_quota.py b/tests/testflows/rbac/tests/syntax/show_create_quota.py
new file mode 100644
index 00000000000..0954a24d2db
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/show_create_quota.py
@@ -0,0 +1,44 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+
+@TestFeature
+@Name("show create quota")
+def feature(self, node="clickhouse1"):
+ """Check show create quota query syntax.
+
+ ```sql
+ SHOW CREATE QUOTA [name | CURRENT]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(quota):
+ try:
+ with Given("I have a quota"):
+ node.query(f"CREATE QUOTA {quota}")
+ yield
+ finally:
+ with Finally("I drop the quota"):
+ node.query(f"DROP QUOTA IF EXISTS {quota}")
+
+ with Scenario("I show create quota", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_ShowCreateQuota_Name("1.0")]):
+ with cleanup("quota0"):
+ with When("I run show create quota command"):
+ node.query("SHOW CREATE QUOTA quota0")
+
+ with Scenario("I show create quota current", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_ShowCreateQuota_Current("1.0")]):
+ with cleanup("quota1"):
+ with When("I run show create quota command"):
+ node.query("SHOW CREATE QUOTA CURRENT")
+
+ with Scenario("I show create quota current short form", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_ShowCreateQuota_Current("1.0")]):
+ with cleanup("quota2"):
+ with When("I run show create quota command"):
+ node.query("SHOW CREATE QUOTA")
diff --git a/tests/testflows/rbac/tests/syntax/show_create_role.py b/tests/testflows/rbac/tests/syntax/show_create_role.py
new file mode 100644
index 00000000000..d7b77bce944
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/show_create_role.py
@@ -0,0 +1,39 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+import rbac.tests.errors as errors
+
+@TestFeature
+@Name("show create role")
+def feature(self, node="clickhouse1"):
+ """Check show create role query syntax.
+
+ ```sql
+ SHOW CREATE ROLE name
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def setup(role):
+ try:
+ with Given("I have a role"):
+ node.query(f"CREATE ROLE OR REPLACE {role}")
+ yield
+ finally:
+ with Finally("I drop the role"):
+ node.query(f"DROP ROLE IF EXISTS {role}")
+
+ with Scenario("I show create role", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_ShowCreate("1.0")]):
+ with setup("role0"):
+ with When("I run show create role command"):
+ node.query("SHOW CREATE ROLE role0")
+
+ with Scenario("I show create role, role doesn't exist, exception", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Role_ShowCreate("1.0")]):
+ with When("I run show create role to catch an exception"):
+ exitcode, message = errors.role_not_found_in_disk(name="role0")
+ node.query("SHOW CREATE ROLE role0", exitcode=exitcode, message=message)
\ No newline at end of file
diff --git a/tests/testflows/rbac/tests/syntax/show_create_row_policy.py b/tests/testflows/rbac/tests/syntax/show_create_row_policy.py
new file mode 100644
index 00000000000..5d8b104540c
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/show_create_row_policy.py
@@ -0,0 +1,51 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+
+@TestFeature
+@Name("show create row policy")
+def feature(self, node="clickhouse1"):
+ """Check show create row policy query syntax.
+
+ ```sql
+ SHOW CREATE [ROW] POLICY name ON [database.]table
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(policy, on="default.foo"):
+ try:
+ with Given("I have a row policy"):
+ node.query(f"CREATE ROW POLICY {policy} ON {on}")
+ yield
+ finally:
+ with Finally("I drop the row policy"):
+ node.query(f"DROP ROW POLICY IF EXISTS {policy} ON {on}")
+
+ try:
+ with Given("I have a table"):
+ node.query(f"CREATE TABLE default.foo (x UInt64, y String) Engine=Memory")
+
+ with Scenario("I show create row policy", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_ShowCreateRowPolicy("1.0")]):
+ with cleanup("policy0"):
+ with When("I run show create row policy command"):
+ node.query("SHOW CREATE ROW POLICY policy0 ON default.foo")
+
+ with Scenario("I show create row policy on a table", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_ShowCreateRowPolicy_On("1.0")]):
+ with cleanup("policy0"):
+ with When("I run show create row policy command"):
+ node.query("SHOW CREATE ROW POLICY policy0 ON default.foo")
+
+ with Scenario("I show create row policy using short syntax on a table", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_ShowCreateRowPolicy_On("1.0")]):
+ with cleanup("policy1",on="foo"):
+ with When("I run show create row policy command"):
+ node.query("SHOW CREATE POLICY policy1 ON foo")
+ finally:
+ with Finally("I drop the table"):
+ node.query(f"DROP TABLE IF EXISTS default.foo")
diff --git a/tests/testflows/rbac/tests/syntax/show_create_settings_profile.py b/tests/testflows/rbac/tests/syntax/show_create_settings_profile.py
new file mode 100644
index 00000000000..6f715463539
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/show_create_settings_profile.py
@@ -0,0 +1,38 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+
+@TestFeature
+@Name("show create settings profile")
+def feature(self, node="clickhouse1"):
+ """Check show create settings profile query syntax.
+
+ ```sql
+ SHOW CREATE [SETTINGS] PROFILE name
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(profile):
+ try:
+ with Given("I have a settings profile"):
+ node.query(f"CREATE SETTINGS PROFILE {profile}")
+ yield
+ finally:
+ with Finally("I drop the settings profile"):
+ node.query(f"DROP SETTINGS PROFILE IF EXISTS {profile}")
+
+ with Scenario("I show create settings profile", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_ShowCreateSettingsProfile("1.0")]):
+ with cleanup("profile0"):
+ with When("I run show create settings profile command"):
+ node.query("SHOW CREATE SETTINGS PROFILE profile0")
+
+ with Scenario("I show create settings profile short form", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_SettingsProfile_ShowCreateSettingsProfile("1.0")]):
+ with cleanup("profile1"):
+ with When("I run show create settings profile command"):
+ node.query("SHOW CREATE PROFILE profile1")
diff --git a/tests/testflows/rbac/tests/syntax/show_create_user.py b/tests/testflows/rbac/tests/syntax/show_create_user.py
new file mode 100644
index 00000000000..804b7e06959
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/show_create_user.py
@@ -0,0 +1,37 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+
+@TestFeature
+@Name("show create user")
+def feature(self, node="clickhouse1"):
+ """Check show create user query syntax.
+
+ ```
+ SHOW CREATE USER [name | CURRENT_USER]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def setup(user):
+ try:
+ with Given("I have a user"):
+ node.query(f"CREATE USER {user}")
+ yield
+ finally:
+ with Finally("I drop the user"):
+ node.query(f"DROP USER IF EXISTS {user}")
+
+ with Scenario("I run show create on user with no options", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_ShowCreateUser_For("1.0")]):
+ with setup("user0"):
+ with When("I run show create user command"):
+ node.query("SHOW CREATE USER user0")
+
+ with Scenario("I run show create on current user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_User_ShowCreateUser("1.0")]):
+ with When("I show create the current user"):
+ node.query("SHOW CREATE USER CURRENT_USER")
\ No newline at end of file
diff --git a/tests/testflows/rbac/tests/syntax/show_grants.py b/tests/testflows/rbac/tests/syntax/show_grants.py
new file mode 100644
index 00000000000..f6c797a6d76
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/show_grants.py
@@ -0,0 +1,37 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+
+@TestFeature
+@Name("show grants")
+def feature(self, node="clickhouse1"):
+ """Check show grants query syntax.
+
+ ```sql
+ SHOW GRANTS [FOR user_or_role]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def setup(user):
+ try:
+ with Given("I have a user"):
+ node.query(f"CREATE USER {user}")
+ yield
+ finally:
+ with Finally("I drop the user"):
+ node.query(f"DROP USER IF EXISTS {user}")
+
+ with Scenario("I show grants for user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Show_Grants_For("1.0")]):
+ with setup("user0"):
+ with When("I run show grants command"):
+ node.query("SHOW GRANTS FOR user0")
+
+ with Scenario("I show grants for current user", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Show_Grants("1.0")]):
+ with When("I show grants"):
+ node.query("SHOW GRANTS")
\ No newline at end of file
diff --git a/tests/testflows/rbac/tests/syntax/show_quotas.py b/tests/testflows/rbac/tests/syntax/show_quotas.py
new file mode 100644
index 00000000000..4003207354d
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/show_quotas.py
@@ -0,0 +1,50 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+
+@TestFeature
+@Name("show quotas")
+def feature(self, node="clickhouse1"):
+ """Check show quotas query syntax.
+
+ ```sql
+ SHOW QUOTAS
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(quota):
+ try:
+ with Given("I have a quota"):
+ node.query(f"CREATE QUOTA OR REPLACE {quota}")
+ yield
+ finally:
+ with Finally("I drop the quota"):
+ node.query(f"DROP QUOTA IF EXISTS {quota}")
+
+ with Scenario("I show quotas", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_ShowQuotas("1.0")]):
+ with cleanup("quota0"), cleanup("quota1"):
+ with When("I run show quota command"):
+ node.query("SHOW QUOTAS")
+
+ with Scenario("I show quotas into outfile", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_ShowQuotas_IntoOutfile("1.0")]):
+ with cleanup("quota0"), cleanup("quota1"):
+ with When("I run show quota command"):
+ node.query("SHOW QUOTAS INTO OUTFILE 'quotas.txt'")
+
+ with Scenario("I show quotas with format", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_ShowQuotas_Format("1.0")]):
+ with cleanup("quota0"), cleanup("quota1"):
+ with When("I run show quota command"):
+ node.query("SHOW QUOTAS FORMAT TabSeparated")
+
+ with Scenario("I show quotas with settings", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_Quota_ShowQuotas("1.0")]):
+ with cleanup("quota0"), cleanup("quota1"):
+ with When("I run show quota command"):
+ node.query("SHOW QUOTAS SETTINGS max_memory_usage=5")
diff --git a/tests/testflows/rbac/tests/syntax/show_row_policies.py b/tests/testflows/rbac/tests/syntax/show_row_policies.py
new file mode 100644
index 00000000000..2bc1471fbe1
--- /dev/null
+++ b/tests/testflows/rbac/tests/syntax/show_row_policies.py
@@ -0,0 +1,58 @@
+from contextlib import contextmanager
+
+from testflows.core import *
+
+from rbac.requirements import *
+
+@TestFeature
+@Name("show row policies")
+def feature(self, node="clickhouse1"):
+ """Check show row polices query syntax.
+
+ ```sql
+ SHOW [ROW] POLICIES [ON [database.]table]
+ ```
+ """
+ node = self.context.cluster.node(node)
+
+ @contextmanager
+ def cleanup(policy, on="default.foo"):
+ try:
+ with Given("I have a row policy"):
+ node.query(f"CREATE ROW POLICY {policy} ON {on}")
+ yield
+ finally:
+ with Finally("I drop the row policy"):
+ node.query(f"DROP ROW POLICY IF EXISTS {policy} ON {on}")
+
+ try:
+ with Given("I have a table"):
+ node.query(f"CREATE TABLE default.foo (x UInt64, y String) Engine=Memory")
+
+ with Scenario("I show row policies", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_ShowRowPolicies("1.0")]):
+ with cleanup("policy0"):
+ with When("I run drop row policy command"):
+ node.query("SHOW ROW POLICIES")
+
+ with Scenario("I show row policies using short syntax", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_ShowRowPolicies("1.0")]):
+ with cleanup("policy1"):
+ with When("I run drop row policy command"):
+ node.query("SHOW POLICIES")
+
+ with Scenario("I show row policies on a database table", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_ShowRowPolicies_On("1.0")]):
+ with cleanup("policy0"):
+ with When("I run drop row policy command"):
+ node.query("SHOW ROW POLICIES ON default.foo")
+
+ with Scenario("I show row policies on a table", flags=TE, requirements=[
+ RQ_SRS_006_RBAC_RowPolicy_ShowRowPolicies_On("1.0")]):
+ with cleanup("policy0"):
+ with When("I run drop row policy command"):
+ node.query("SHOW ROW POLICIES ON foo")
+
+ finally:
+ with Finally("I drop the table"):
+ node.query(f"DROP TABLE IF EXISTS default.foo")