Skip to content

Commit 88ca9b6

Browse files
authored
Merge pull request #1132 from ethho/dev-tests-plat-160-privileges
PLAT-160: Migrate test_privileges
2 parents 911184f + 88f7b53 commit 88ca9b6

File tree

3 files changed

+163
-5
lines changed

3 files changed

+163
-5
lines changed

tests/conftest.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import datajoint as dj
22
from packaging import version
3+
from typing import Dict
34
import os
45
from os import environ, remove
56
import minio
@@ -56,12 +57,17 @@ def enable_filepath_feature(monkeypatch):
5657

5758

5859
@pytest.fixture(scope="session")
59-
def connection_root_bare():
60-
connection = dj.Connection(
61-
host=os.getenv("DJ_HOST"),
62-
user=os.getenv("DJ_USER"),
63-
password=os.getenv("DJ_PASS"),
60+
def db_creds_root() -> Dict:
61+
return dict(
62+
host=os.getenv("DJ_HOST", "fakeservices.datajoint.io"),
63+
user=os.getenv("DJ_USER", "root"),
64+
password=os.getenv("DJ_PASS", "password"),
6465
)
66+
67+
68+
@pytest.fixture(scope="session")
69+
def connection_root_bare(db_creds_root):
70+
connection = dj.Connection(**db_creds_root)
6571
yield connection
6672

6773

tests/schema_privileges.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import datajoint as dj
2+
import inspect
3+
4+
5+
class Parent(dj.Lookup):
6+
definition = """
7+
id: int
8+
"""
9+
contents = [(1,)]
10+
11+
12+
class Child(dj.Computed):
13+
definition = """
14+
-> Parent
15+
"""
16+
17+
def make(self, key):
18+
self.insert1(key)
19+
20+
21+
class NoAccess(dj.Lookup):
22+
definition = """
23+
string: varchar(10)
24+
"""
25+
26+
27+
class NoAccessAgain(dj.Manual):
28+
definition = """
29+
-> NoAccess
30+
"""
31+
32+
33+
LOCALS_PRIV = {k: v for k, v in locals().items() if inspect.isclass(v)}
34+
__all__ = list(LOCALS_PRIV)

tests/test_privileges.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import os
2+
import pytest
3+
import datajoint as dj
4+
from . import schema, CONN_INFO_ROOT, PREFIX
5+
from . import schema_privileges
6+
7+
namespace = locals()
8+
9+
10+
@pytest.fixture
11+
def schema_priv(connection_test):
12+
schema_priv = dj.Schema(
13+
context=schema_privileges.LOCALS_PRIV,
14+
connection=connection_test,
15+
)
16+
schema_priv(schema_privileges.Parent)
17+
schema_priv(schema_privileges.Child)
18+
schema_priv(schema_privileges.NoAccess)
19+
schema_priv(schema_privileges.NoAccessAgain)
20+
yield schema_priv
21+
if schema_priv.is_activated():
22+
schema_priv.drop()
23+
24+
25+
@pytest.fixture
26+
def connection_djsubset(connection_root, db_creds_root, schema_priv):
27+
user = "djsubset"
28+
conn = dj.conn(**db_creds_root, reset=True)
29+
schema_priv.activate(f"{PREFIX}_schema_privileges")
30+
conn.query(
31+
f"""
32+
CREATE USER IF NOT EXISTS '{user}'@'%%'
33+
IDENTIFIED BY '{user}'
34+
"""
35+
)
36+
conn.query(
37+
f"""
38+
GRANT SELECT, INSERT, UPDATE, DELETE
39+
ON `{PREFIX}_schema_privileges`.`#parent`
40+
TO '{user}'@'%%'
41+
"""
42+
)
43+
conn.query(
44+
f"""
45+
GRANT SELECT, INSERT, UPDATE, DELETE
46+
ON `{PREFIX}_schema_privileges`.`__child`
47+
TO '{user}'@'%%'
48+
"""
49+
)
50+
conn_djsubset = dj.conn(
51+
host=db_creds_root["host"],
52+
user=user,
53+
password=user,
54+
reset=True,
55+
)
56+
yield conn_djsubset
57+
conn.query(f"DROP USER {user}")
58+
conn.query(f"DROP DATABASE {PREFIX}_schema_privileges")
59+
60+
61+
@pytest.fixture
62+
def connection_djview(connection_root, db_creds_root):
63+
"""
64+
A connection with only SELECT privilege to djtest schemas.
65+
Requires connection_root fixture so that `djview` user exists.
66+
"""
67+
connection = dj.conn(
68+
host=db_creds_root["host"],
69+
user="djview",
70+
password="djview",
71+
reset=True,
72+
)
73+
yield connection
74+
75+
76+
class TestUnprivileged:
77+
def test_fail_create_schema(self, connection_djview):
78+
"""creating a schema with no CREATE privilege"""
79+
with pytest.raises(dj.DataJointError):
80+
return dj.Schema(
81+
"forbidden_schema", namespace, connection=connection_djview
82+
)
83+
84+
def test_insert_failure(self, connection_djview, schema_any):
85+
unprivileged = dj.Schema(
86+
schema_any.database, namespace, connection=connection_djview
87+
)
88+
unprivileged.spawn_missing_classes()
89+
assert issubclass(Language, dj.Lookup) and len(Language()) == len(
90+
schema.Language()
91+
), "failed to spawn missing classes"
92+
with pytest.raises(dj.DataJointError):
93+
Language().insert1(("Socrates", "Greek"))
94+
95+
def test_failure_to_create_table(self, connection_djview, schema_any):
96+
unprivileged = dj.Schema(
97+
schema_any.database, namespace, connection=connection_djview
98+
)
99+
100+
@unprivileged
101+
class Try(dj.Manual):
102+
definition = """ # should not matter really
103+
id : int
104+
---
105+
value : float
106+
"""
107+
108+
with pytest.raises(dj.DataJointError):
109+
Try().insert1((1, 1.5))
110+
111+
112+
class TestSubset:
113+
def test_populate_activate(self, connection_djsubset, schema_priv):
114+
schema_priv.activate(
115+
f"{PREFIX}_schema_privileges", create_schema=True, create_tables=False
116+
)
117+
schema_privileges.Child.populate()
118+
assert schema_privileges.Child.progress(display=False)[0] == 0

0 commit comments

Comments
 (0)