Skip to content
This repository was archived by the owner on Apr 29, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 58 additions & 26 deletions conference/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
from enum import Enum
import json
from functools import wraps
from hashlib import md5
from django.conf.urls import url as re_path
from django.contrib.auth.hashers import check_password
from django.contrib.auth.hashers import check_password as django_check_password
from django.contrib.auth.hashers import is_password_usable
from django.db.models import Q
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
Expand All @@ -29,6 +31,7 @@
)
from pycon.settings import MATRIX_AUTH_API_DEBUG as DEBUG
from pycon.settings import MATRIX_AUTH_API_ALLOWED_IPS as ALLOWED_IPS
from pycon.settings import SECRET_KEY


# Error Codes
Expand Down Expand Up @@ -113,6 +116,57 @@ def wrapper(request, *args, **kwargs):
return wrapper


def check_user_password(user, password):
# Two options: either our User has a valid password, in which case we do
# check it, or not, in which case we check it against the generated passwd.
if not is_password_usable(user.password):
return password == generate_matrix_password(user)
return django_check_password(password, user.password)


def get_assigned_tickets(user, conference):
return Ticket.objects.filter(
Q(fare__conference=conference.code)
& Q(frozen=False) # i.e. the ticket was not cancelled
& Q(orderitem__order___complete=True) # i.e. they paid
& Q(user=user) # i.e. assigned to user
)


def is_speaker(user, conference):
# A speaker is a user with at least one accepted talk in the current
# conference.
try:
speaker = user.speaker
except Speaker.DoesNotExist:
return False
return TalkSpeaker.objects.filter(
speaker=speaker,
talk__conference=conference.code,
talk__status='accepted'
).count() > 0


def generate_matrix_password(user):
"""
Create a temporary password for `user` to that they can login into our
matrix chat server using their email address and that password. This is
only needed for social auth users since they do not have a valid password
in our database.

The generated passowrd is not stored anywhere.
"""
def n_base_b(n, b, nums='0123456789abcdefghijklmnopqrstuvwxyz'):
"""Return `n` in base `b`."""

return ((n == 0) and nums[0]) or \
(n_base_b(n // b, b, nums).lstrip(nums[0]) + nums[n % b])

encoded = md5(str(user.email + SECRET_KEY).encode()).hexdigest()
n = int(encoded, 16)
return n_base_b(n, 36)


@csrf_exempt
@ensure_post
@ensure_https_in_ops
Expand Down Expand Up @@ -171,44 +225,22 @@ def isauth(request):
return _error(ApiError.AUTH_ERROR, 'unknown user')

# Is the password OK?
if not check_password(data['password'], profile.user.password):
if not check_user_password(profile.user, data['password']):
return _error(ApiError.AUTH_ERROR, 'authentication error')

# Get the tickets **assigned** to the user
conference = Conference.objects.current()

tickets = Ticket.objects.filter(
Q(fare__conference=conference.code)
& Q(frozen=False) # i.e. the ticket was not cancelled
& Q(orderitem__order___complete=True) # i.e. they paid
& Q(user=profile.user) # i.e. assigned to user
)

# A speaker is a user with at least one accepted talk in the current
# conference.
try:
speaker = profile.user.speaker
except Speaker.DoesNotExist:
is_speaker = False
else:
is_speaker = TalkSpeaker.objects.filter(
speaker=speaker,
talk__conference=conference.code,
talk__status='accepted'
).count() > 0

payload = {
"username": profile.user.username,
"first_name": profile.user.first_name,
"last_name": profile.user.last_name,
"email": profile.user.email,
"is_staff": profile.user.is_staff,
"is_speaker": is_speaker,
"is_speaker": is_speaker(profile.user, conference),
"is_active": profile.user.is_active,
"is_minor": profile.is_minor,
"tickets": [
{"fare_name": t.fare.name, "fare_code": t.fare.code}
for t in tickets
for t in get_assigned_tickets(profile.user, conference)
]
}

Expand Down
24 changes: 24 additions & 0 deletions conference/user_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from django.conf import settings
from django.conf.urls import url as re_path
from django.contrib import messages
from django.contrib.auth.hashers import is_password_usable
from django.contrib.auth import views as auth_views
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
Expand All @@ -27,6 +28,7 @@
from p3.utils import assign_ticket_to_user

from .accounts import get_or_create_attendee_profile_for_new_user
from .api import generate_matrix_password
from .cfp import AddSpeakerToTalkForm
from .models import (
AttendeeProfile,
Expand All @@ -43,6 +45,18 @@
from .decorators import full_profile_required


def fare_valid_for_matrix_login(user, ticket):
"""
Return whether or not `ticket` allows `user` to access our Matrix chat
server.

The conditions are:
1. `ticket` is *assigned* to `user` AND
2. `ticket.fare.code` is in (TRCC, TRCP, TRSC, TRSP, TRPC, TRPP)
"""
return ticket.user == user and ticket.fare.code[2] in 'CSP'


@login_required
@full_profile_required
def user_dashboard(request):
Expand All @@ -51,6 +65,14 @@ def user_dashboard(request):
invoices = get_invoices_for_current_conference(request.user)
tickets = get_tickets_for_current_conference(request.user)

matrix_username = None
matrix_password = None
if any(fare_valid_for_matrix_login(request.user, t) for t in tickets):
matrix_username = request.user.email
matrix_password = request.user.password \
if is_password_usable(request.user.password) \
else generate_matrix_password(request.user)

return TemplateResponse(
request,
"conference/user_panel/dashboard.html",
Expand All @@ -63,6 +85,8 @@ def user_dashboard(request):
"orders": orders,
"invoices": invoices,
"tickets": tickets,
"matrix_username": matrix_username,
"matrix_password": matrix_password,
},
)

Expand Down
5 changes: 0 additions & 5 deletions templates/conference/schedule/schedule.html
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,6 @@
</div>
{% endcomment %}

<div class="alert alert-danger text-center">
For links to the sessions, please click on the track headers in Discord.
Those will then take you to the Zoom rooms and webinars.
</div>

<div class="alert alert-primary text-center">
The scheduled times are displayed in your local timezone as configured
in your browser (<span class="schedule-timezone-display">UTC</span>).
Expand Down
33 changes: 31 additions & 2 deletions templates/conference/user_panel/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,36 @@ <h2 style='margin-top: 2em'>Proposals</h2>
<a href="{% page_url 'submit-proposal' %}">If you'd like to do this, here's more information</a>
{% endif %}

<h2 style='margin-top: 2em'>EuroPython Conference System</h2>
{% if not matrix_username and not matrix_password %}
Sorry, you need Sprint Only, a Conference a Combined ticket to access the
EuroPython Matrix Chat server.
{% else %}
You can access the
<a href="https://matrix.europython.eu/" target="matrix">EuroPython Conference System</a>
using these credentials:<br /><br />

<table class="table">
<tr>
<th>Email</th><th>Password</th>
</tr>
<tr>
<td>{{ matrix_username }}</td>
<td>
{% if matrix_password == user.password %}
The same password you use to login into this site
{% else %}
{{ matrix_password }}
{% endif %}
</td>
</tr>
</table>
<a class='btn btn-primary' href="https://matrix.europython.eu/" target="matrix">
Go to the EuroPython Conference System Now
</a>

{% endif %}

<h2 style='margin-top: 2em'>Tickets</h2>
{% if tickets %}
{% include "conference/user_panel/_tickets.html" %}
Expand All @@ -43,7 +73,6 @@ <h2 style='margin-top: 2em'>Tickets</h2>
<a href="{% page_url 'tickets' %}">Click here to see what tickets we have available right now.</a>
{% endif %}


<h2 style='margin-top: 2em'>Invoices</h2>
{% if invoices %}
{% include "conference/user_panel/_invoices.html" %}
Expand All @@ -56,4 +85,4 @@ <h2 style='margin-top: 2em'>Invoices</h2>
<div class="col-md-4">
</div>
</div>
{% endblock %}
{% endblock %}