Skip to content

Commit 7cced50

Browse files
committed
Require full email address to share investigation with individual users
1 parent e6ff6a2 commit 7cced50

File tree

7 files changed

+184
-9
lines changed

7 files changed

+184
-9
lines changed

aleph/tests/test_permissions_api.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
from aleph.tests.util import TestCase
2+
from aleph.logic.roles import create_group
3+
4+
5+
class PermissionsApiTestCase(TestCase):
6+
def setUp(self):
7+
super().setUp()
8+
self.role, self.headers = self.login(
9+
foreign_id="john",
10+
name="John Doe",
11+
email="john.doe@example.org",
12+
)
13+
self.col = self.create_collection(creator=self.role)
14+
15+
def test_update(self):
16+
jane = self.create_user(
17+
foreign_id="jane",
18+
name="Jane Doe",
19+
email="jane.doe@example.org",
20+
)
21+
22+
url = f"/api/2/collections/{self.col.id}/permissions"
23+
res = self.client.get(url, headers=self.headers)
24+
assert len(res.json["results"]) == 1
25+
assert res.json["results"][0]["role"]["id"] == str(self.role.id)
26+
27+
# Granting a new user access without providing their full email address is ignored
28+
data = [
29+
{"role_id": str(self.role.id), "read": True, "write": True},
30+
{"role_id": str(jane.id), "read": True, "write": False},
31+
]
32+
res = self.client.put(url, headers=self.headers, json=data)
33+
assert res.status_code == 200
34+
assert len(res.json["results"]) == 1
35+
assert res.json["results"][0]["role"]["id"] == str(self.role.id)
36+
37+
# Granting a new user accces with an incorrect email address is ignored
38+
data = [
39+
{
40+
"role_id": str(self.role.id),
41+
"read": True,
42+
"write": True,
43+
},
44+
{
45+
"role_id": str(jane.id),
46+
"email": "thisisnotjane@example.org",
47+
"read": True,
48+
"write": False,
49+
},
50+
]
51+
res = self.client.put(url, headers=self.headers, json=data)
52+
assert res.status_code == 200
53+
assert len(res.json["results"]) == 1
54+
assert res.json["results"][0]["role"]["id"] == str(self.role.id)
55+
56+
# Granting a new user access updates permissions if full email address is provided
57+
data = [
58+
{
59+
"role_id": str(self.role.id),
60+
"read": True,
61+
"write": False,
62+
},
63+
{
64+
"role_id": str(jane.id),
65+
"email": "jane.doe@example.org",
66+
"read": True,
67+
"write": False,
68+
},
69+
]
70+
res = self.client.put(url, headers=self.headers, json=data)
71+
assert res.status_code == 200
72+
assert len(res.json["results"]) == 2
73+
assert res.json["results"][0]["role"]["id"] == str(self.role.id)
74+
assert res.json["results"][1]["role"]["id"] == str(jane.id)
75+
assert res.json["results"][1]["read"] is True
76+
assert res.json["results"][1]["write"] is False
77+
78+
def test_update_groups(self):
79+
group = create_group("group")
80+
81+
url = f"/api/2/collections/{self.col.id}/permissions"
82+
res = self.client.get(url, headers=self.headers)
83+
assert len(res.json["results"]) == 1
84+
assert res.json["results"][0]["role"]["id"] == str(self.role.id)
85+
86+
# Updated permissions for a group the user is not a member of are ignored
87+
data = [
88+
{"role_id": str(self.role.id), "read": True, "write": True},
89+
{"role_id": str(group.id), "read": True, "write": False},
90+
]
91+
res = self.client.put(url, headers=self.headers, json=data)
92+
assert res.status_code == 200
93+
assert len(res.json["results"]) == 1
94+
assert res.json["results"][0]["role"]["id"] == str(self.role.id)
95+
96+
self.role.add_role(group)
97+
res = self.client.put(url, headers=self.headers, json=data)
98+
assert res.status_code == 200
99+
assert len(res.json["results"]) == 2
100+
assert res.json["results"][0]["role"]["id"] == str(self.role.id)
101+
assert res.json["results"][1]["role"]["id"] == str(group.id)
102+
assert res.json["results"][1]["read"] is True
103+
assert res.json["results"][1]["write"] is False

aleph/tests/test_roles_api.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,55 @@ def setUp(self):
1717
def test_suggest(self):
1818
res = self.client.get("/api/2/roles/_suggest")
1919
assert res.status_code == 403, res
20-
_, headers = self.login(is_admin=True)
21-
res = self.client.get("/api/2/roles/_suggest?prefix=user", headers=headers)
22-
assert res.status_code == 200, res
23-
assert res.json["total"] >= 3, res.json
20+
21+
_, headers = self.login(foreign_id="jane", email="jane.doe@example.org")
22+
john, _ = self.login(foreign_id="john", email="john.doe@example.org")
23+
24+
res = self.client.get(
25+
"/api/2/roles/_suggest",
26+
headers=headers,
27+
)
28+
assert res.status_code == 200
29+
assert res.json["total"] == 0
30+
assert res.json["results"] == []
31+
32+
res = self.client.get(
33+
"/api/2/roles/_suggest",
34+
query_string={"prefix": "john"},
35+
headers=headers,
36+
)
37+
assert res.status_code == 200
38+
assert res.json["total"] == 0
39+
assert res.json["results"] == []
40+
41+
res = self.client.get(
42+
"/api/2/roles/_suggest",
43+
query_string={"prefix": "john.doe@example.org"},
44+
headers=headers,
45+
)
46+
assert res.status_code == 200
47+
assert res.json["total"] == 1
48+
assert len(res.json["results"]) == 1
49+
assert res.json["results"][0]["id"] == str(john.id)
50+
51+
res = self.client.get(
52+
"/api/2/roles/_suggest",
53+
query_string={"prefix": "JOHN.DOE@EXAMPLE.org"},
54+
headers=headers,
55+
)
56+
assert res.status_code == 200
57+
assert res.json["total"] == 1
58+
assert len(res.json["results"]) == 1
59+
assert res.json["results"][0]["id"] == str(john.id)
60+
61+
res = self.client.get(
62+
"/api/2/roles/_suggest",
63+
query_string={"prefix": "john.doe@example.org", "exclude:id": john.id},
64+
headers=headers,
65+
)
66+
assert res.status_code == 200
67+
assert res.json["total"] == 0
68+
assert len(res.json["results"]) == 0
2469

2570
def test_view(self):
2671
res = self.client.get("/api/2/roles/%s" % self.rolex)

aleph/validation/schema/permission.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ PermissionUpdate:
2020
type: boolean
2121
role_id:
2222
type: string
23+
email:
24+
type: string
2325
role:
2426
$ref: "#/components/schemas/Role"
2527

aleph/views/permissions_api.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,27 @@ def update(collection_id):
117117
- Collection
118118
"""
119119
collection = get_db_collection(collection_id, request.authz.WRITE)
120+
current = Permission.all().where(Permission.collection_id == collection.id)
121+
current = [permission.role_id for permission in current]
122+
120123
for permission in parse_request("PermissionUpdateList"):
121124
role_obj = ensure_dict(permission.get("role"))
122125
role_id = permission.get("role_id", role_obj.get("id"))
123126
role = Role.by_id(role_id)
127+
124128
if not check_visible(role, request.authz):
125129
continue
130+
131+
if (
132+
role.type == Role.USER
133+
and role.id not in current
134+
and (not permission.get("email") or role.email != permission.get("email"))
135+
):
136+
continue
137+
126138
if role.is_public:
127139
permission["write"] = False
140+
128141
if collection.casefile and role.is_public:
129142
permission["read"] = False
130143

@@ -135,6 +148,7 @@ def update(collection_id):
135148
permission["write"],
136149
editor_id=request.authz.id,
137150
)
151+
138152
collection.updated_at = datetime.utcnow()
139153
update_collection(collection)
140154
db.session.commit()

aleph/views/roles_api.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from flask import Blueprint, request
55
from itsdangerous import BadSignature
66
from werkzeug.exceptions import BadRequest
7+
from sqlalchemy import func
78

89
from aleph.core import db
910
from aleph.authz import Authz
@@ -66,8 +67,12 @@ def suggest():
6667
)
6768
# this only returns users, not groups
6869
exclude = ensure_list(parser.excludes.get("id"))
69-
q = Role.by_prefix(parser.prefix, exclude=exclude)
70-
result = DatabaseQueryResult(request, q, parser=parser)
70+
query = (
71+
Role.all_users()
72+
.where(Role.id.not_in(exclude))
73+
.where(func.lower(Role.email) == parser.prefix.lower())
74+
)
75+
result = DatabaseQueryResult(request, query, parser=parser)
7176
return RoleSerializer.jsonify_result(result)
7277

7378

ui/src/components/common/Role.jsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ const messages = defineMessages({
1616
id: 'role.select.user',
1717
defaultMessage: 'Choose a user',
1818
},
19+
placeholder: {
20+
id: 'role.select.placeholder',
21+
defaultMessage: 'Enter email address…',
22+
},
1923
});
2024

2125
class RoleLabel extends PureComponent {
@@ -94,13 +98,14 @@ class Select extends Component {
9498
const { exclude = [] } = this.props;
9599
const roles = await this.props.suggestRoles(query, exclude);
96100
this.setState({
101+
query,
97102
suggested: roles.results,
98103
});
99104
}
100105

101106
onSelectRole(role, event) {
102107
event.stopPropagation();
103-
this.props.onSelect(role);
108+
this.props.onSelect(role, this.state.query);
104109
}
105110

106111
renderRole = (role, { handleClick, modifiers }) => (
@@ -130,6 +135,7 @@ class Select extends Component {
130135
}}
131136
inputProps={{
132137
fill: true,
138+
placeholder: intl.formatMessage(messages.placeholder),
133139
}}
134140
activeItem={role}
135141
filterable={!isFixed}

ui/src/dialogs/CollectionAccessDialog/CollectionAccessDialog.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ class CollectionAccessDialog extends Component {
8888
}
8989
}
9090

91-
onAddRole(role) {
91+
onAddRole(role, query) {
9292
const { permissions } = this.state;
93-
permissions.push({ role, read: true, write: false });
93+
permissions.push({ role, email: query, read: true, write: false });
9494
this.setState({ permissions });
9595
}
9696

0 commit comments

Comments
 (0)