Skip to content

Commit a171b72

Browse files
authored
Allow site admin to view other tree details (#467)
* Allow site admin to view other tree details * Fix test
1 parent bd9bfe9 commit a171b72

File tree

4 files changed

+56
-13
lines changed

4 files changed

+56
-13
lines changed

gramps_webapi/api/resources/trees.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import os
2323
import re
2424
import uuid
25-
from typing import Dict, Optional
25+
from typing import Dict, List, Optional
2626

2727
from flask import abort, current_app
2828
from gramps.gen.config import config
@@ -36,11 +36,12 @@
3636
PERM_EDIT_OTHER_TREE,
3737
PERM_EDIT_TREE,
3838
PERM_EDIT_TREE_QUOTA,
39+
PERM_VIEW_OTHER_TREE,
3940
)
4041
from ...const import TREE_MULTI
4142
from ...dbmanager import WebDbManager
42-
from ..auth import require_permissions
43-
from ..util import abort_with_message, get_tree_from_jwt, use_args
43+
from ..auth import has_permissions, require_permissions
44+
from ..util import abort_with_message, get_tree_from_jwt, use_args, list_trees
4445
from . import ProtectedResource
4546

4647
# legal tree dirnames
@@ -79,14 +80,23 @@ def validate_tree_id(tree_id: str) -> None:
7980
abort_with_message(422, "Invalid tree ID")
8081

8182

83+
def get_tree_ids() -> List[str]:
84+
"""Get a list of all existing tree IDs."""
85+
tree_details = list_trees()
86+
return [os.path.basename(details[1]) for details in tree_details]
87+
88+
8289
class TreesResource(ProtectedResource):
8390
"""Resource for getting info about trees."""
8491

8592
def get(self):
8693
"""Get info about all trees."""
87-
user_tree_id = get_tree_from_jwt()
88-
# only allowed to see details about our own tree
89-
tree_ids = [user_tree_id]
94+
if has_permissions([PERM_VIEW_OTHER_TREE]):
95+
tree_ids = get_tree_ids()
96+
else:
97+
# only allowed to see details about our own tree
98+
user_tree_id = get_tree_from_jwt()
99+
tree_ids = [user_tree_id]
90100
return [get_tree_details(tree_id) for tree_id in tree_ids]
91101

92102
@use_args(
@@ -129,10 +139,11 @@ def get(self, tree_id: str):
129139
tree_id = get_tree_from_jwt()
130140
else:
131141
validate_tree_id(tree_id)
132-
user_tree_id = get_tree_from_jwt()
133-
if tree_id != user_tree_id:
134-
# only allowed to see details about our own tree
135-
abort_with_message(403, "Not authorized to view other trees")
142+
if not has_permissions([PERM_VIEW_OTHER_TREE]):
143+
user_tree_id = get_tree_from_jwt()
144+
if tree_id != user_tree_id:
145+
# only allowed to see details about our own tree
146+
abort_with_message(403, "Not authorized to view other trees")
136147
return get_tree_details(tree_id)
137148

138149
@use_args(

gramps_webapi/auth/const.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
PERM_DEL_OTHER_TREE_USER = "DeleteOtherTreeUser"
4040
PERM_EDIT_USER_TREE = "EditUserTree"
4141
PERM_EDIT_OTHER_TREE = "EditOtherTree"
42+
PERM_VIEW_OTHER_TREE = "ViewOtherTree"
4243
PERM_ADD_TREE = "AddTree"
4344
PERM_EDIT_TREE_QUOTA = "EditTreeQuota"
4445
PERM_DISABLE_TREE = "DisableTree"
@@ -108,6 +109,7 @@
108109
PERM_DEL_OTHER_TREE_USER,
109110
PERM_VIEW_SETTINGS,
110111
PERM_EDIT_SETTINGS,
112+
PERM_VIEW_OTHER_TREE,
111113
PERM_EDIT_OTHER_TREE,
112114
PERM_EDIT_TREE_QUOTA,
113115
PERM_ADD_TREE,

gramps_webapi/data/apispec.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6894,6 +6894,8 @@ paths:
68946894
$ref: "#/definitions/Tree"
68956895
401:
68966896
description: "Unauthorized: Missing authorization header."
6897+
403:
6898+
description: "Unauthorized: insufficient permissions."
68976899
404:
68986900
description: "Not found: tree does not exist."
68996901
422:

tests/test_endpoints/test_trees.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def tearDown(self):
6969
self.ctx.pop()
7070
self.dbman.remove_database(self.name)
7171

72-
def test_list_trees(self):
72+
def test_list_trees_admin(self):
7373
rv = self.client.post(
7474
BASE_URL + "/token/", json={"username": "admin", "password": "123"}
7575
)
@@ -78,21 +78,49 @@ def test_list_trees(self):
7878
BASE_URL + "/trees/", headers={"Authorization": f"Bearer {token}"}
7979
)
8080
assert rv.status_code == 200
81-
assert rv.json == [{"name": self.name, "id": self.tree}]
81+
trees = rv.json
82+
trees_dict = {tree["id"]: tree["name"] for tree in trees}
83+
assert trees_dict[self.tree] == self.name
84+
# admin can see more trees
85+
assert len(trees) > 1
86+
87+
def test_list_trees_owner(self):
88+
rv = self.client.post(
89+
BASE_URL + "/token/", json={"username": "owner", "password": "123"}
90+
)
91+
token = rv.json["access_token"]
92+
rv = self.client.get(
93+
BASE_URL + "/trees/", headers={"Authorization": f"Bearer {token}"}
94+
)
95+
assert rv.status_code == 200
96+
trees = rv.json
97+
# owner can see only one tree
98+
assert trees == [{"id": self.tree, "name": self.name}]
8299

83100
def test_get_tree(self):
84101
rv = self.client.post(
85102
BASE_URL + "/token/", json={"username": "admin", "password": "123"}
86103
)
87104
token = rv.json["access_token"]
105+
rv = self.client.post(
106+
BASE_URL + "/token/", json={"username": "owner", "password": "123"}
107+
)
108+
token_owner = rv.json["access_token"]
88109
rv = self.client.get(
89110
BASE_URL + "/trees/..", headers={"Authorization": f"Bearer {token}"}
90111
)
91112
assert rv.status_code == 422
113+
# owner should get a 403 for non existing tree - not authorized
92114
rv = self.client.get(
93-
BASE_URL + "/trees/notexist", headers={"Authorization": f"Bearer {token}"}
115+
BASE_URL + "/trees/notexist",
116+
headers={"Authorization": f"Bearer {token_owner}"},
94117
)
95118
assert rv.status_code == 403
119+
# admin should get 404 thouogh
120+
rv = self.client.get(
121+
BASE_URL + "/trees/notexist", headers={"Authorization": f"Bearer {token}"}
122+
)
123+
assert rv.status_code == 404
96124
rv = self.client.get(
97125
BASE_URL + f"/trees/{self.tree}",
98126
headers={"Authorization": f"Bearer {token}"},

0 commit comments

Comments
 (0)