14
14
from email .mime .text import MIMEText
15
15
from common .lib .helpers import send_email
16
16
from common .lib .exceptions import DataSetException
17
- from common .config_manager import config
17
+ from common .config_manager import config as global_config
18
18
19
19
20
20
class User :
@@ -28,12 +28,13 @@ class User:
28
28
is_authenticated = False
29
29
is_active = False
30
30
is_anonymous = True
31
+ config = None
31
32
db = None
32
33
33
34
name = "anonymous"
34
35
35
36
@staticmethod
36
- def get_by_login (db , name , password ):
37
+ def get_by_login (db , name , password , config = None ):
37
38
"""
38
39
Get user object, if login is correct
39
40
@@ -43,6 +44,8 @@ def get_by_login(db, name, password):
43
44
:param db: Database connection object
44
45
:param name: User name
45
46
: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.
46
49
:return: User object, or `None` if login was invalid
47
50
"""
48
51
user = db .fetchone ("SELECT * FROM users WHERE name = %s" , (name ,))
@@ -54,30 +57,34 @@ def get_by_login(db, name, password):
54
57
return None
55
58
else :
56
59
# valid login!
57
- return User (db , user , authenticated = True )
60
+ return User (db , user , authenticated = True , config = config )
58
61
59
62
@staticmethod
60
- def get_by_name (db , name ):
63
+ def get_by_name (db , name , config = None ):
61
64
"""
62
65
Get user object for given user name
63
66
64
67
:param db: Database connection object
65
68
: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.
66
71
:return: User object, or `None` for invalid user name
67
72
"""
68
73
user = db .fetchone ("SELECT * FROM users WHERE name = %s" , (name ,))
69
74
if not user :
70
75
return None
71
76
else :
72
- return User (db , user )
77
+ return User (db , user , config = config )
73
78
74
79
@staticmethod
75
- def get_by_token (db , token ):
80
+ def get_by_token (db , token , config = None ):
76
81
"""
77
82
Get user object for given token, if token is valid
78
83
79
84
:param db: Database connection object
80
85
: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.
81
88
:return: User object, or `None` for invalid token
82
89
"""
83
90
user = db .fetchone (
@@ -86,36 +93,9 @@ def get_by_token(db, token):
86
93
if not user :
87
94
return None
88
95
else :
89
- return User (db , user )
96
+ return User (db , user , config = config )
90
97
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 ):
119
99
"""
120
100
Instantiate user object
121
101
@@ -127,6 +107,9 @@ def __init__(self, db, data, authenticated=False):
127
107
"""
128
108
self .db = db
129
109
self .data = data
110
+
111
+ self .config = config if config else global_config
112
+
130
113
try :
131
114
self .userdata = json .loads (self .data ["userdata" ])
132
115
except (TypeError , json .JSONDecodeError ):
@@ -170,7 +153,7 @@ def get_name(self):
170
153
if self .data ["name" ] == "anonymous" :
171
154
return "Anonymous"
172
155
elif self .data ["name" ] == "autologin" :
173
- return config .get ("flask.autologin.name" )
156
+ return self . config .get ("flask.autologin.name" )
174
157
else :
175
158
return self .data ["name" ]
176
159
@@ -184,6 +167,21 @@ def get_token(self):
184
167
"""
185
168
return self .generate_token (regenerate = False )
186
169
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
+
187
185
def clear_token (self ):
188
186
"""
189
187
Reset password rest token
@@ -195,6 +193,33 @@ def clear_token(self):
195
193
"""
196
194
self .db .update ("users" , data = {"register_token" : "" , "timestamp_token" : 0 }, where = {"name" : self .get_id ()})
197
195
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
+
198
223
@property
199
224
def is_special (self ):
200
225
"""
@@ -246,7 +271,7 @@ def email_token(self, new=False):
246
271
account?
247
272
:return str: Link for the user to set their password with
248
273
"""
249
- if not config .get ('mail.server' ):
274
+ if not self . config .get ('mail.server' ):
250
275
raise RuntimeError ("No e-mail server configured. 4CAT cannot send any e-mails." )
251
276
252
277
if self .is_special :
@@ -258,14 +283,14 @@ def email_token(self, new=False):
258
283
register_token = self .generate_token (regenerate = True )
259
284
260
285
# prepare welcome e-mail
261
- sender = config .get ('mail.noreply' )
286
+ sender = self . config .get ('mail.noreply' )
262
287
message = MIMEMultipart ("alternative" )
263
288
message ["From" ] = sender
264
289
message ["To" ] = username
265
290
266
291
# 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"
269
294
url = "%s://%s/reset-password/?token=%s" % (protocol , url_base , register_token )
270
295
271
296
# 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):
408
433
409
434
:return list: Notifications, as a list of dictionaries
410
435
"""
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 ) ]]
412
437
if self .is_admin :
413
438
# for backwards compatibility - used to be called '!admins' even if the tag is 'admin'
414
439
tag_recipients .append ("!admins" )
@@ -457,7 +482,7 @@ def sort_user_tags(self):
457
482
tags = self .data ["tags" ]
458
483
sorted_tags = []
459
484
460
- for tag in config .get ("flask.tag_order" ):
485
+ for tag in self . config .get ("flask.tag_order" ):
461
486
if tag in tags :
462
487
sorted_tags .append (tag )
463
488
0 commit comments