Skip to content

Commit e5a54b3

Browse files
committed
editing in progress, authorization moved to auth.py
1 parent d8836d5 commit e5a54b3

File tree

7 files changed

+259
-410
lines changed

7 files changed

+259
-410
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"python.analysis.typeCheckingMode": "off"
3+
}

sogs/authentication/auth.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
from ..web import app
2+
from ..db import query
3+
from .. import config, crypto, http, utils
4+
from ..model.user import User
5+
from ..model.room import Room
6+
7+
from typing import Optional
8+
9+
10+
class Authority:
11+
def __init__(self):
12+
self._perm_cache = {}
13+
14+
def check_permission_for(
15+
self,
16+
room: Room,
17+
user: Optional[User] = None,
18+
*,
19+
admin=False,
20+
moderator=False,
21+
read=False,
22+
accessible=False,
23+
write=False,
24+
upload=False,
25+
):
26+
"""
27+
Checks whether `user` has the required permissions for this room and isn't banned. Returns
28+
True if the user satisfies the permissions, False otherwise. If no user is provided then
29+
permissions are checked against the room's defaults.
30+
31+
Looked up permissions are cached within the Room instance so that looking up the same user
32+
multiple times (i.e. from multiple parts of the code) does not re-query the database.
33+
34+
Named arguments are as follows:
35+
- admin -- if true then the user must have admin access to the room
36+
- moderator -- if true then the user must have moderator (or admin) access to the room
37+
- read -- if true then the user must have read access
38+
- accessible -- if true then the user must have accessible access; note that this permission
39+
is satisfied by *either* the `accessible` or `read` database flags (that is: read implies
40+
accessible).
41+
- write -- if true then the user must have write access
42+
- upload -- if true then the user must have upload access; this should usually be combined
43+
with write=True.
44+
45+
You can specify multiple permissions as True, in which case all must be satisfied. If you
46+
specify no permissions as required then the check only checks whether a user is banned but
47+
otherwise requires no specific permission.
48+
"""
49+
50+
if user is None:
51+
is_banned, can_read, can_access, can_write, can_upload, is_mod, is_admin = (
52+
False,
53+
bool(room.default_read),
54+
bool(room.default_accessible),
55+
bool(room.default_write),
56+
bool(room.default_upload),
57+
False,
58+
False,
59+
)
60+
else:
61+
if user.id not in self._perm_cache:
62+
row = query(
63+
"""
64+
SELECT banned, read, accessible, write, upload, moderator, admin
65+
FROM user_permissions
66+
WHERE room = :r AND "user" = :u
67+
""",
68+
r=self.id,
69+
u=user.id,
70+
).first()
71+
self._perm_cache[user.id] = [bool(c) for c in row]
72+
73+
(
74+
is_banned,
75+
can_read,
76+
can_access,
77+
can_write,
78+
can_upload,
79+
is_mod,
80+
is_admin,
81+
) = self._perm_cache[user.id]
82+
83+
# Shortcuts for check_permission calls
84+
def check_unbanned(self, room: Room, user: Optional[User]):
85+
return self.check_permission_for(room, user)
86+
87+
def check_read(self, room: Room, user: Optional[User] = None):
88+
return self.check_permission_for(room, user, read=True)
89+
90+
def check_accessible(self, room: Room, user: Optional[User] = None):
91+
return self.check_permission_for(room, user, accessible=True)
92+
93+
def check_write(self, room: Room, user: Optional[User] = None):
94+
return self.check_permission_for(room, user, write=True)
95+
96+
def check_upload(self, room: Room, user: Optional[User] = None):
97+
"""Checks for both upload *and* write permission"""
98+
return self.check_permission_for(room, user, write=True, upload=True)
99+
100+
def check_moderator(self, room: Room, user: Optional[User]):
101+
return self.check_permission_for(room, user, moderator=True)
102+
103+
def check_admin(self, room: Room, user: Optional[User]):
104+
return self.check_permission_for(room, user, admin=True)

sogs/model/bot.py

Lines changed: 0 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ class Bot:
2525
status - bot active(T)/passive(F) moderation status (key = room, value = bool)
2626
rooms - reference to room(s) in which bot is active
2727
filter - reference to filter object paired with bot
28-
perm_cache - dict storing user info/status (synced from all rooms patrolled)
2928
session_id - hex encoded session_id of the bot
3029
banned - default to false
3130
global_admin - default to true for bot
@@ -79,189 +78,3 @@ def _unlink_room(self, _room: Room):
7978

8079
if not self.rooms:
8180
delete_bot_function_goes_here = True
82-
83-
def _refresh_cache(self):
84-
for r in self.rooms:
85-
self.perm_cache = self.perm_cache | r._perm_cache
86-
87-
def check_permission_for(
88-
self,
89-
room: Room,
90-
user: Optional[User] = None,
91-
*,
92-
admin=False,
93-
moderator=False,
94-
read=False,
95-
accessible=False,
96-
write=False,
97-
upload=False,
98-
):
99-
"""
100-
Checks whether `user` has the required permissions for this room and isn't banned. Returns
101-
True if the user satisfies the permissions, False otherwise. If no user is provided then
102-
permissions are checked against the room's defaults.
103-
104-
Looked up permissions are cached within the Room instance so that looking up the same user
105-
multiple times (i.e. from multiple parts of the code) does not re-query the database.
106-
107-
Named arguments are as follows:
108-
- admin -- if true then the user must have admin access to the room
109-
- moderator -- if true then the user must have moderator (or admin) access to the room
110-
- read -- if true then the user must have read access
111-
- accessible -- if true then the user must have accessible access; note that this permission
112-
is satisfied by *either* the `accessible` or `read` database flags (that is: read implies
113-
accessible).
114-
- write -- if true then the user must have write access
115-
- upload -- if true then the user must have upload access; this should usually be combined
116-
with write=True.
117-
118-
You can specify multiple permissions as True, in which case all must be satisfied. If you
119-
specify no permissions as required then the check only checks whether a user is banned but
120-
otherwise requires no specific permission.
121-
"""
122-
123-
if user is None:
124-
is_banned, can_read, can_access, can_write, can_upload, is_mod, is_admin = (
125-
False,
126-
bool(room.default_read),
127-
bool(room.default_accessible),
128-
bool(room.default_write),
129-
bool(room.default_upload),
130-
False,
131-
False,
132-
)
133-
else:
134-
if user.id not in self._perm_cache:
135-
row = query(
136-
"""
137-
SELECT banned, read, accessible, write, upload, moderator, admin
138-
FROM user_permissions
139-
WHERE room = :r AND "user" = :u
140-
""",
141-
r=self.id,
142-
u=user.id,
143-
).first()
144-
self._perm_cache[user.id] = [bool(c) for c in row]
145-
146-
(
147-
is_banned,
148-
can_read,
149-
can_access,
150-
can_write,
151-
can_upload,
152-
is_mod,
153-
is_admin,
154-
) = self._perm_cache[user.id]
155-
156-
# Shortcuts for check_permission calls
157-
def check_unbanned(self, room: Room, user: Optional[User]):
158-
return self.check_permission_for(room, user)
159-
160-
def check_read(self, room: Room, user: Optional[User] = None):
161-
return self.check_permission_for(room, user, read=True)
162-
163-
def check_accessible(self, room: Room, user: Optional[User] = None):
164-
return self.check_permission_for(room, user, accessible=True)
165-
166-
def check_write(self, room: Room, user: Optional[User] = None):
167-
return self.check_permission_for(room, user, write=True)
168-
169-
def check_upload(self, room: Room, user: Optional[User] = None):
170-
"""Checks for both upload *and* write permission"""
171-
return self.check_permission_for(room, user, write=True, upload=True)
172-
173-
def check_moderator(self, room: Room, user: Optional[User]):
174-
return self.check_permission_for(room, user, moderator=True)
175-
176-
def check_admin(self, room: Room, user: Optional[User]):
177-
return self.check_permission_for(room, user, admin=True)
178-
179-
def receive_message(
180-
self,
181-
room: Room,
182-
user: User,
183-
data: bytes,
184-
sig: bytes,
185-
*,
186-
whisper_to: Optional[Union[User, str]] = None,
187-
whisper_mods: bool = False,
188-
files: List[int] = [],
189-
):
190-
if not self.check_write(user):
191-
raise BadPermission()
192-
193-
if data is None or sig is None or len(sig) != 64:
194-
raise InvalidData()
195-
196-
whisper_mods = bool(whisper_mods)
197-
if (whisper_to or whisper_mods) and not self.check_moderator(user):
198-
app.logger.warning(f"Cannot post a whisper to {room}: {user} is not a moderator")
199-
raise BadPermission()
200-
201-
if whisper_to and not isinstance(whisper_to, User):
202-
whisper_to = User(session_id=whisper_to, autovivify=True, touch=False)
203-
204-
filtered = self.filter.read_message(user, data, room)
205-
206-
with db.transaction():
207-
if room.rate_limit_size and not self.check_admin(user):
208-
since_limit = time.time() - room.rate_limit_interval
209-
recent_count = query(
210-
"""
211-
SELECT COUNT(*) FROM messages
212-
WHERE room = :r AND "user" = :u AND posted >= :since
213-
""",
214-
r=self.id,
215-
u=user.id,
216-
since=since_limit,
217-
).first()[0]
218-
219-
if recent_count >= room.rate_limit_size:
220-
raise PostRateLimited()
221-
222-
data_size = len(data)
223-
unpadded_data = utils.remove_session_message_padding(data)
224-
225-
msg_id = db.insert_and_get_pk(
226-
"""
227-
INSERT INTO messages
228-
(room, "user", data, data_size, signature, filtered, whisper, whisper_mods)
229-
VALUES
230-
(:r, :u, :data, :data_size, :signature, :filtered, :whisper, :whisper_mods)
231-
""",
232-
"id",
233-
r=room.id,
234-
u=user.id,
235-
data=unpadded_data,
236-
data_size=data_size,
237-
signature=sig,
238-
filtered=filtered is not None,
239-
whisper=whisper_to.id if whisper_to else None,
240-
whisper_mods=whisper_mods,
241-
)
242-
243-
if files:
244-
# Take ownership of any uploaded files attached to the post:
245-
room._own_files(msg_id, files, user)
246-
247-
assert msg_id is not None
248-
row = query("SELECT posted, seqno FROM messages WHERE id = :m", m=msg_id).first()
249-
msg = {
250-
'id': msg_id,
251-
'session_id': user.session_id,
252-
'posted': row[0],
253-
'seqno': row[1],
254-
'data': data,
255-
'signature': sig,
256-
'reactions': {},
257-
}
258-
if filtered is not None:
259-
msg['filtered'] = True
260-
if whisper_to or whisper_mods:
261-
msg['whisper'] = True
262-
msg['whisper_mods'] = whisper_mods
263-
if whisper_to:
264-
msg['whisper_to'] = whisper_to.session_id
265-
266-
omq_global.send_mule("message_posted", msg["id"])
267-
return msg

0 commit comments

Comments
 (0)