Skip to content
This repository was archived by the owner on Apr 29, 2022. It is now read-only.

Commit 764d1d0

Browse files
Matrix login (#1422)
* clarified cfp submission steps and moved AddSpeakerToTalkForm into talks.py file * Fixed tests to better reflect new changes * added i_accept_speaker_release to test_update_speaker_updated_speaker_name * bolded both steps for clarity * removed "of the speaker" from labels on the form * removed print statement * first working version * more in line with the rest of the page * Added sprint ticket holders to those who can access matrix * We are not using Discord this year * small clarification in the docstring Co-authored-by: ethan <[email protected]> Co-authored-by: Ethan Carlsson <[email protected]>
1 parent 3e91873 commit 764d1d0

File tree

4 files changed

+113
-33
lines changed

4 files changed

+113
-33
lines changed

conference/api.py

Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
from enum import Enum
1616
import json
1717
from functools import wraps
18+
from hashlib import md5
1819
from django.conf.urls import url as re_path
19-
from django.contrib.auth.hashers import check_password
20+
from django.contrib.auth.hashers import check_password as django_check_password
21+
from django.contrib.auth.hashers import is_password_usable
2022
from django.db.models import Q
2123
from django.http import JsonResponse
2224
from django.views.decorators.csrf import csrf_exempt
@@ -29,6 +31,7 @@
2931
)
3032
from pycon.settings import MATRIX_AUTH_API_DEBUG as DEBUG
3133
from pycon.settings import MATRIX_AUTH_API_ALLOWED_IPS as ALLOWED_IPS
34+
from pycon.settings import SECRET_KEY
3235

3336

3437
# Error Codes
@@ -113,6 +116,57 @@ def wrapper(request, *args, **kwargs):
113116
return wrapper
114117

115118

119+
def check_user_password(user, password):
120+
# Two options: either our User has a valid password, in which case we do
121+
# check it, or not, in which case we check it against the generated passwd.
122+
if not is_password_usable(user.password):
123+
return password == generate_matrix_password(user)
124+
return django_check_password(password, user.password)
125+
126+
127+
def get_assigned_tickets(user, conference):
128+
return Ticket.objects.filter(
129+
Q(fare__conference=conference.code)
130+
& Q(frozen=False) # i.e. the ticket was not cancelled
131+
& Q(orderitem__order___complete=True) # i.e. they paid
132+
& Q(user=user) # i.e. assigned to user
133+
)
134+
135+
136+
def is_speaker(user, conference):
137+
# A speaker is a user with at least one accepted talk in the current
138+
# conference.
139+
try:
140+
speaker = user.speaker
141+
except Speaker.DoesNotExist:
142+
return False
143+
return TalkSpeaker.objects.filter(
144+
speaker=speaker,
145+
talk__conference=conference.code,
146+
talk__status='accepted'
147+
).count() > 0
148+
149+
150+
def generate_matrix_password(user):
151+
"""
152+
Create a temporary password for `user` to that they can login into our
153+
matrix chat server using their email address and that password. This is
154+
only needed for social auth users since they do not have a valid password
155+
in our database.
156+
157+
The generated passowrd is not stored anywhere.
158+
"""
159+
def n_base_b(n, b, nums='0123456789abcdefghijklmnopqrstuvwxyz'):
160+
"""Return `n` in base `b`."""
161+
162+
return ((n == 0) and nums[0]) or \
163+
(n_base_b(n // b, b, nums).lstrip(nums[0]) + nums[n % b])
164+
165+
encoded = md5(str(user.email + SECRET_KEY).encode()).hexdigest()
166+
n = int(encoded, 16)
167+
return n_base_b(n, 36)
168+
169+
116170
@csrf_exempt
117171
@ensure_post
118172
@ensure_https_in_ops
@@ -171,44 +225,22 @@ def isauth(request):
171225
return _error(ApiError.AUTH_ERROR, 'unknown user')
172226

173227
# Is the password OK?
174-
if not check_password(data['password'], profile.user.password):
228+
if not check_user_password(profile.user, data['password']):
175229
return _error(ApiError.AUTH_ERROR, 'authentication error')
176230

177-
# Get the tickets **assigned** to the user
178231
conference = Conference.objects.current()
179-
180-
tickets = Ticket.objects.filter(
181-
Q(fare__conference=conference.code)
182-
& Q(frozen=False) # i.e. the ticket was not cancelled
183-
& Q(orderitem__order___complete=True) # i.e. they paid
184-
& Q(user=profile.user) # i.e. assigned to user
185-
)
186-
187-
# A speaker is a user with at least one accepted talk in the current
188-
# conference.
189-
try:
190-
speaker = profile.user.speaker
191-
except Speaker.DoesNotExist:
192-
is_speaker = False
193-
else:
194-
is_speaker = TalkSpeaker.objects.filter(
195-
speaker=speaker,
196-
talk__conference=conference.code,
197-
talk__status='accepted'
198-
).count() > 0
199-
200232
payload = {
201233
"username": profile.user.username,
202234
"first_name": profile.user.first_name,
203235
"last_name": profile.user.last_name,
204236
"email": profile.user.email,
205237
"is_staff": profile.user.is_staff,
206-
"is_speaker": is_speaker,
238+
"is_speaker": is_speaker(profile.user, conference),
207239
"is_active": profile.user.is_active,
208240
"is_minor": profile.is_minor,
209241
"tickets": [
210242
{"fare_name": t.fare.name, "fare_code": t.fare.code}
211-
for t in tickets
243+
for t in get_assigned_tickets(profile.user, conference)
212244
]
213245
}
214246

conference/user_panel.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from django.conf import settings
1111
from django.conf.urls import url as re_path
1212
from django.contrib import messages
13+
from django.contrib.auth.hashers import is_password_usable
1314
from django.contrib.auth import views as auth_views
1415
from django.contrib.auth.decorators import login_required
1516
from django.contrib.auth.models import User
@@ -27,6 +28,7 @@
2728
from p3.utils import assign_ticket_to_user
2829

2930
from .accounts import get_or_create_attendee_profile_for_new_user
31+
from .api import generate_matrix_password
3032
from .cfp import AddSpeakerToTalkForm
3133
from .models import (
3234
AttendeeProfile,
@@ -43,6 +45,18 @@
4345
from .decorators import full_profile_required
4446

4547

48+
def fare_valid_for_matrix_login(user, ticket):
49+
"""
50+
Return whether or not `ticket` allows `user` to access our Matrix chat
51+
server.
52+
53+
The conditions are:
54+
1. `ticket` is *assigned* to `user` AND
55+
2. `ticket.fare.code` is in (TRCC, TRCP, TRSC, TRSP, TRPC, TRPP)
56+
"""
57+
return ticket.user == user and ticket.fare.code[2] in 'CSP'
58+
59+
4660
@login_required
4761
@full_profile_required
4862
def user_dashboard(request):
@@ -51,6 +65,14 @@ def user_dashboard(request):
5165
invoices = get_invoices_for_current_conference(request.user)
5266
tickets = get_tickets_for_current_conference(request.user)
5367

68+
matrix_username = None
69+
matrix_password = None
70+
if any(fare_valid_for_matrix_login(request.user, t) for t in tickets):
71+
matrix_username = request.user.email
72+
matrix_password = request.user.password \
73+
if is_password_usable(request.user.password) \
74+
else generate_matrix_password(request.user)
75+
5476
return TemplateResponse(
5577
request,
5678
"conference/user_panel/dashboard.html",
@@ -63,6 +85,8 @@ def user_dashboard(request):
6385
"orders": orders,
6486
"invoices": invoices,
6587
"tickets": tickets,
88+
"matrix_username": matrix_username,
89+
"matrix_password": matrix_password,
6690
},
6791
)
6892

templates/conference/schedule/schedule.html

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -228,11 +228,6 @@
228228
</div>
229229
{% endcomment %}
230230

231-
<div class="alert alert-danger text-center">
232-
For links to the sessions, please click on the track headers in Discord.
233-
Those will then take you to the Zoom rooms and webinars.
234-
</div>
235-
236231
<div class="alert alert-primary text-center">
237232
The scheduled times are displayed in your local timezone as configured
238233
in your browser (<span class="schedule-timezone-display">UTC</span>).

templates/conference/user_panel/dashboard.html

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,36 @@ <h2 style='margin-top: 2em'>Proposals</h2>
3535
<a href="{% page_url 'submit-proposal' %}">If you'd like to do this, here's more information</a>
3636
{% endif %}
3737

38+
<h2 style='margin-top: 2em'>EuroPython Conference System</h2>
39+
{% if not matrix_username and not matrix_password %}
40+
Sorry, you need Sprint Only, a Conference a Combined ticket to access the
41+
EuroPython Matrix Chat server.
42+
{% else %}
43+
You can access the
44+
<a href="https://matrix.europython.eu/" target="matrix">EuroPython Conference System</a>
45+
using these credentials:<br /><br />
46+
47+
<table class="table">
48+
<tr>
49+
<th>Email</th><th>Password</th>
50+
</tr>
51+
<tr>
52+
<td>{{ matrix_username }}</td>
53+
<td>
54+
{% if matrix_password == user.password %}
55+
The same password you use to login into this site
56+
{% else %}
57+
{{ matrix_password }}
58+
{% endif %}
59+
</td>
60+
</tr>
61+
</table>
62+
<a class='btn btn-primary' href="https://matrix.europython.eu/" target="matrix">
63+
Go to the EuroPython Conference System Now
64+
</a>
65+
66+
{% endif %}
67+
3868
<h2 style='margin-top: 2em'>Tickets</h2>
3969
{% if tickets %}
4070
{% include "conference/user_panel/_tickets.html" %}
@@ -43,7 +73,6 @@ <h2 style='margin-top: 2em'>Tickets</h2>
4373
<a href="{% page_url 'tickets' %}">Click here to see what tickets we have available right now.</a>
4474
{% endif %}
4575

46-
4776
<h2 style='margin-top: 2em'>Invoices</h2>
4877
{% if invoices %}
4978
{% include "conference/user_panel/_invoices.html" %}
@@ -56,4 +85,4 @@ <h2 style='margin-top: 2em'>Invoices</h2>
5685
<div class="col-md-4">
5786
</div>
5887
</div>
59-
{% endblock %}
88+
{% endblock %}

0 commit comments

Comments
 (0)