Skip to content

Commit 58549e4

Browse files
Add test recording permissions API behaviour (#3900)
## Changes Add test recording permissions API behaviour wrt multiple levels for the same principal. ## Why Document behaviour (important for permissions processing). I also plan to use this test to fix the testserver. --------- Co-authored-by: shreyas-goenka <[email protected]>
1 parent 3a52448 commit 58549e4

File tree

6 files changed

+183
-0
lines changed

6 files changed

+183
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import sys
2+
import os
3+
import pprint
4+
import json
5+
import subprocess
6+
7+
8+
cli = os.environ["CLI"]
9+
10+
11+
class Error(Exception):
12+
pass
13+
14+
15+
def run_json(cmd):
16+
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8")
17+
if result.returncode != 0:
18+
if result.stderr.strip():
19+
raise Error(result.stderr.strip())
20+
raise Error(f"{cmd} failed with code {result.returncode}")
21+
try:
22+
return json.loads(result.stdout)
23+
except Exception as ex:
24+
raise Error(f"{cmd} returned non-json: {ex}\n{result.stdout}")
25+
26+
27+
def test_permissions(target_user, resource_type, resource_id, levels, expected):
28+
"""Set permissions on a given resource/principal with a given set of levels. Verify that resulting levels match expected."""
29+
acls = []
30+
if resource_type == "jobs" and target_user != os.environ["CURRENT_USER_NAME"]:
31+
# make sure we have IS_OWNER, otherwise backend returns an error
32+
acls.append({"service_principal_name": os.environ["CURRENT_USER_NAME"], "permission_level": "IS_OWNER"})
33+
34+
for level in levels.split(","):
35+
acls.append({"user_name": target_user, "permission_level": level})
36+
37+
request = {"access_control_list": acls}
38+
39+
try:
40+
set_result = run_json([cli, "permissions", "set", resource_type, resource_id, "--json", json.dumps(request)])
41+
except Error as ex:
42+
sys.exit(f"{resource_type} {levels} => SET ERROR {ex}\nREQUEST:\n" + pprint.pformat(request))
43+
44+
get_result = run_json([cli, "permissions", "get", resource_type, resource_id])
45+
46+
if set_result != get_result:
47+
print("set() response is different from get() response")
48+
print("set:")
49+
pprint.pprint(set_result)
50+
print("get:")
51+
pprint.pprint(get_result)
52+
53+
resulting_levels = []
54+
55+
for item in set_result["access_control_list"]:
56+
if (item.get("user_name") or item.get("service_principal_name")) != target_user:
57+
continue
58+
for perm in item["all_permissions"]:
59+
if perm.get("inherited"):
60+
continue
61+
resulting_levels.append(perm["permission_level"])
62+
63+
resulting_levels = ", ".join(resulting_levels)
64+
if expected == resulting_levels:
65+
print(f"{resource_type} {levels} => {resulting_levels}")
66+
else:
67+
print(f"{resource_type} {levels} => {resulting_levels}; EXPECTED: {expected}")
68+
print("REQUEST")
69+
pprint.pprint(request)
70+
print("RESPONSE")
71+
pprint.pprint(set_result)
72+
print()
73+
74+
75+
def main():
76+
test_permissions(*sys.argv[1:])
77+
78+
79+
if __name__ == "__main__":
80+
main()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
bundle:
2+
name: test_bundle_$UNIQUE_NAME
3+
4+
resources:
5+
jobs:
6+
job:
7+
name: job1
8+
tasks:
9+
- task_key: main
10+
notebook_task:
11+
notebook_path: /Workspace/Users/[email protected]/notebook
12+
source: WORKSPACE
13+
new_cluster:
14+
num_workers: 1
15+
num_workers: 1
16+
spark_version: $DEFAULT_SPARK_VERSION
17+
node_type_id: $NODE_TYPE_ID
18+
19+
sql_warehouses:
20+
warehouse:
21+
name: foo_$UNIQUE_NAME
22+
cluster_size: Small

acceptance/bundle/resources/permissions/factcheck/out.test.toml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
>>> [CLI] bundle deploy
3+
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test_bundle_[UNIQUE_NAME]/default/files...
4+
Deploying resources...
5+
Updating deployment state...
6+
Deployment complete!
7+
8+
=== Permissions API simply takes the latest level in the request for a given principal
9+
jobs CAN_VIEW,CAN_MANAGE_RUN => CAN_MANAGE_RUN
10+
jobs CAN_MANAGE_RUN,CAN_VIEW => CAN_VIEW
11+
jobs CAN_VIEW,CAN_MANAGE_RUN,CAN_MANAGE => CAN_MANAGE
12+
jobs CAN_MANAGE,CAN_MANAGE_RUN => CAN_MANAGE_RUN
13+
jobs CAN_MANAGE,IS_OWNER => IS_OWNER
14+
15+
=== Since we take the most recent level, IS_OWNER is lost, which results in the error due to lack of owner defined
16+
jobs IS_OWNER,CAN_MANAGE => SET ERROR Error: The job must have exactly one owner.
17+
REQUEST:
18+
{'access_control_list': [{'permission_level': 'IS_OWNER',
19+
'user_name': '[USERNAME]'},
20+
{'permission_level': 'CAN_MANAGE',
21+
'user_name': '[USERNAME]'}]}
22+
sql/warehouses CAN_VIEW,CAN_USE => CAN_USE
23+
sql/warehouses CAN_USE,CAN_VIEW => CAN_VIEW
24+
25+
>>> [CLI] bundle destroy --auto-approve
26+
The following resources will be deleted:
27+
delete job job
28+
delete sql_warehouse warehouse
29+
30+
All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/test_bundle_[UNIQUE_NAME]/default
31+
32+
Deleting files...
33+
Destroy complete!
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
envsubst < databricks.yml.tmpl > databricks.yml
2+
3+
cleanup() {
4+
trace $CLI bundle destroy --auto-approve
5+
}
6+
trap cleanup EXIT
7+
8+
9+
trace $CLI bundle deploy
10+
11+
job_id=$($CLI bundle summary --output json | jq -r '.resources.jobs.job.id')
12+
echo "$job_id:JOB_ID" >> ACC_REPLS
13+
14+
sql_warehouse_id=$($CLI bundle summary --output json | jq -r '.resources.sql_warehouses.warehouse.id')
15+
echo "$sql_warehouse_id:SQL_WAREHOUSE_ID" >> ACC_REPLS
16+
17+
18+
19+
title "Permissions API simply takes the latest level in the request for a given principal\n"
20+
python3 check_permissions.py $user jobs $job_id CAN_VIEW,CAN_MANAGE_RUN CAN_MANAGE_RUN
21+
python3 check_permissions.py $user jobs $job_id CAN_MANAGE_RUN,CAN_VIEW CAN_VIEW
22+
python3 check_permissions.py $user jobs $job_id CAN_VIEW,CAN_MANAGE_RUN,CAN_MANAGE CAN_MANAGE
23+
python3 check_permissions.py $user jobs $job_id CAN_MANAGE,CAN_MANAGE_RUN CAN_MANAGE_RUN
24+
25+
python3 check_permissions.py $CURRENT_USER_NAME jobs $job_id CAN_MANAGE,IS_OWNER IS_OWNER
26+
27+
title "Since we take the most recent level, IS_OWNER is lost, which results in the error due to lack of owner defined\n"
28+
musterr python3 check_permissions.py $CURRENT_USER_NAME jobs $job_id IS_OWNER,CAN_MANAGE CAN_MANAGE
29+
30+
python3 check_permissions.py $user sql/warehouses $sql_warehouse_id CAN_VIEW,CAN_USE CAN_USE
31+
python3 check_permissions.py $user sql/warehouses $sql_warehouse_id CAN_USE,CAN_VIEW CAN_VIEW
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Local = false
2+
Cloud = true
3+
RecordRequests = false
4+
5+
# I get this error, not sure why:
6+
# -=== Since we take the most recent level, IS_OWNER is lost, which results in the error due to lack of owner defined
7+
# -jobs IS_OWNER,CAN_MANAGE => SET ERROR Error: The job must have exactly one owner.
8+
# +jobs CAN_VIEW,CAN_MANAGE_RUN => SET ERROR Error: [USERNAME]: Service[USERNAME]Name([USERNAME]) does not exist
9+
CloudEnvs.gcp = false

0 commit comments

Comments
 (0)