Skip to content

Commit b21866d

Browse files
committed
Ensure request-aware config reader in user object when using config wrapper
1 parent bbe79e4 commit b21866d

File tree

2 files changed

+71
-42
lines changed

2 files changed

+71
-42
lines changed

common/config_manager.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,10 @@ def __init__(self, config, user=None, tags=None, request=None):
437437
self.tags = tags
438438
self.request = request
439439

440+
# this ensures the user object in turn reads from the wrapper
441+
if self.user:
442+
self.user.with_config(self)
443+
440444

441445
def set(self, *args, **kwargs):
442446
"""

common/lib/user.py

Lines changed: 67 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from email.mime.text import MIMEText
1515
from common.lib.helpers import send_email
1616
from common.lib.exceptions import DataSetException
17-
from common.config_manager import config
17+
from common.config_manager import config as global_config
1818

1919

2020
class User:
@@ -28,12 +28,13 @@ class User:
2828
is_authenticated = False
2929
is_active = False
3030
is_anonymous = True
31+
config = None
3132
db = None
3233

3334
name = "anonymous"
3435

3536
@staticmethod
36-
def get_by_login(db, name, password):
37+
def get_by_login(db, name, password, config=None):
3738
"""
3839
Get user object, if login is correct
3940
@@ -43,6 +44,8 @@ def get_by_login(db, name, password):
4344
:param db: Database connection object
4445
:param name: User name
4546
:param password: User password
47+
:param config: Configuration manager. Can be used for request-aware user objects using ConfigWrapper. Empty to
48+
use a global configuration manager.
4649
:return: User object, or `None` if login was invalid
4750
"""
4851
user = db.fetchone("SELECT * FROM users WHERE name = %s", (name,))
@@ -54,30 +57,34 @@ def get_by_login(db, name, password):
5457
return None
5558
else:
5659
# valid login!
57-
return User(db, user, authenticated=True)
60+
return User(db, user, authenticated=True, config=config)
5861

5962
@staticmethod
60-
def get_by_name(db, name):
63+
def get_by_name(db, name, config=None):
6164
"""
6265
Get user object for given user name
6366
6467
:param db: Database connection object
6568
:param str name: Username to get object for
69+
:param config: Configuration manager. Can be used for request-aware user objects using ConfigWrapper. Empty to
70+
use a global configuration manager.
6671
:return: User object, or `None` for invalid user name
6772
"""
6873
user = db.fetchone("SELECT * FROM users WHERE name = %s", (name,))
6974
if not user:
7075
return None
7176
else:
72-
return User(db, user)
77+
return User(db, user, config=config)
7378

7479
@staticmethod
75-
def get_by_token(db, token):
80+
def get_by_token(db, token, config=None):
7681
"""
7782
Get user object for given token, if token is valid
7883
7984
:param db: Database connection object
8085
:param str token: Token to get object for
86+
:param config: Configuration manager. Can be used for request-aware user objects using ConfigWrapper. Empty to
87+
use a global configuration manager.
8188
:return: User object, or `None` for invalid token
8289
"""
8390
user = db.fetchone(
@@ -86,36 +93,9 @@ def get_by_token(db, token):
8693
if not user:
8794
return None
8895
else:
89-
return User(db, user)
96+
return User(db, user, config=config)
9097

91-
def can_access_dataset(self, dataset, role=None):
92-
"""
93-
Check if this user should be able to access a given dataset.
94-
95-
This depends mostly on the dataset's owner, which should match the
96-
user if the dataset is private. If the dataset is not private, or
97-
if the user is an admin or the dataset is private but assigned to
98-
an anonymous user, the dataset can be accessed.
99-
100-
:param dataset: The dataset to check access to
101-
:return bool:
102-
"""
103-
if not dataset.is_private:
104-
return True
105-
106-
elif self.is_admin:
107-
return True
108-
109-
elif dataset.is_accessible_by(self, role=role):
110-
return True
111-
112-
elif dataset.get_owners == ("anonymous",):
113-
return True
114-
115-
else:
116-
return False
117-
118-
def __init__(self, db, data, authenticated=False):
98+
def __init__(self, db, data, authenticated=False, config=None):
11999
"""
120100
Instantiate user object
121101
@@ -127,6 +107,9 @@ def __init__(self, db, data, authenticated=False):
127107
"""
128108
self.db = db
129109
self.data = data
110+
111+
self.config = config if config else global_config
112+
130113
try:
131114
self.userdata = json.loads(self.data["userdata"])
132115
except (TypeError, json.JSONDecodeError):
@@ -170,7 +153,7 @@ def get_name(self):
170153
if self.data["name"] == "anonymous":
171154
return "Anonymous"
172155
elif self.data["name"] == "autologin":
173-
return config.get("flask.autologin.name")
156+
return self.config.get("flask.autologin.name")
174157
else:
175158
return self.data["name"]
176159

@@ -184,6 +167,21 @@ def get_token(self):
184167
"""
185168
return self.generate_token(regenerate=False)
186169

170+
def with_config(self, config):
171+
"""
172+
Connect user to configuration manager
173+
174+
By default, the user object reads from the global configuration
175+
manager. For frontend operations it may be desireable to use a
176+
request-aware configuration manager, but this is only available after
177+
the user has been instantiated. This method can thus be used to connect
178+
the user to that config manager later when it is available.
179+
180+
:param config: Configuration manager object
181+
:return:
182+
"""
183+
self.config = config
184+
187185
def clear_token(self):
188186
"""
189187
Reset password rest token
@@ -195,6 +193,33 @@ def clear_token(self):
195193
"""
196194
self.db.update("users", data={"register_token": "", "timestamp_token": 0}, where={"name": self.get_id()})
197195

196+
def can_access_dataset(self, dataset, role=None):
197+
"""
198+
Check if this user should be able to access a given dataset.
199+
200+
This depends mostly on the dataset's owner, which should match the
201+
user if the dataset is private. If the dataset is not private, or
202+
if the user is an admin or the dataset is private but assigned to
203+
an anonymous user, the dataset can be accessed.
204+
205+
:param dataset: The dataset to check access to
206+
:return bool:
207+
"""
208+
if not dataset.is_private:
209+
return True
210+
211+
elif self.is_admin:
212+
return True
213+
214+
elif dataset.is_accessible_by(self, role=role):
215+
return True
216+
217+
elif dataset.get_owners == ("anonymous",):
218+
return True
219+
220+
else:
221+
return False
222+
198223
@property
199224
def is_special(self):
200225
"""
@@ -246,7 +271,7 @@ def email_token(self, new=False):
246271
account?
247272
:return str: Link for the user to set their password with
248273
"""
249-
if not config.get('mail.server'):
274+
if not self.config.get('mail.server'):
250275
raise RuntimeError("No e-mail server configured. 4CAT cannot send any e-mails.")
251276

252277
if self.is_special:
@@ -258,14 +283,14 @@ def email_token(self, new=False):
258283
register_token = self.generate_token(regenerate=True)
259284

260285
# prepare welcome e-mail
261-
sender = config.get('mail.noreply')
286+
sender = self.config.get('mail.noreply')
262287
message = MIMEMultipart("alternative")
263288
message["From"] = sender
264289
message["To"] = username
265290

266291
# the actual e-mail...
267-
url_base = config.get("flask.server_name")
268-
protocol = "https" if config.get("flask.https") else "http"
292+
url_base = self.config.get("flask.server_name")
293+
protocol = "https" if self.config.get("flask.https") else "http"
269294
url = "%s://%s/reset-password/?token=%s" % (protocol, url_base, register_token)
270295

271296
# we use slightly different e-mails depending on whether this is the first time setting a password
@@ -408,7 +433,7 @@ def get_notifications(self):
408433
409434
:return list: Notifications, as a list of dictionaries
410435
"""
411-
tag_recipients = ["!everyone", *[f"!{tag}" for tag in self.data["tags"]]]
436+
tag_recipients = ["!everyone", *[f"!{tag}" for tag in self.config.get_active_tags(self)]]
412437
if self.is_admin:
413438
# for backwards compatibility - used to be called '!admins' even if the tag is 'admin'
414439
tag_recipients.append("!admins")
@@ -457,7 +482,7 @@ def sort_user_tags(self):
457482
tags = self.data["tags"]
458483
sorted_tags = []
459484

460-
for tag in config.get("flask.tag_order"):
485+
for tag in self.config.get("flask.tag_order"):
461486
if tag in tags:
462487
sorted_tags.append(tag)
463488

0 commit comments

Comments
 (0)