Skip to content

Commit

Permalink
Switch authentication to Dreamjub
Browse files Browse the repository at this point in the history
Previously, authentication worked by transmitting the password to the
legacy OpenJUB API. Furthermore user data was also received from this
API. This caused problem, as Jay was able potentially able to intercept
user passwords and also depended on a legacy system.

This commit updates the authentication to use the new dreamjub api via
OAuth. Furthermore, it updates the permission system to move away from
UserProfiles and SuperAdmin models. Instead, this commit makes use of internal
Django mechanisms and stores user data directly with the model.

See also issue #26.
  • Loading branch information
kuboschek authored and tkw1536 committed Nov 1, 2017
1 parent b7dca4d commit bf81a03
Show file tree
Hide file tree
Showing 17 changed files with 155 additions and 288 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ Jay is a simple secret voting system for Jacobs University. Just in case you wer

see [doc/](doc) for minimal developer documentation

## Setup

1. Configure database and apply migrations
2. Create a Social Application pointing to `dreamjub`

## License

Jay is © 2015-17 Leonhard Kuboschek, Tom Wiesing & Contributors. Licensed under MIT license. See [LICENSE.md](LICENSE.md) for details.
7 changes: 5 additions & 2 deletions core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from settings.models import VotingSystem
from votes.models import Vote, Status
from jay.utils import get_user_details


# Create your views here.
Expand All @@ -14,8 +15,10 @@ def home(request):
systems = VotingSystem.objects.all()

if request.user.is_authenticated():
details = json.loads(request.user.profile.details)
votes_shown = [v for v in votes if v.filter.matches(details)]
details = get_user_details(request.user)

votes_shown = [v for v in votes if v.filter.matches(json.loads(
details))]

ctx["vote_list_title"] = "Your votes"

Expand Down
4 changes: 2 additions & 2 deletions filters/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from settings.models import VotingSystem

import filters.forest as forest
from jay import utils


# Create your models here.
Expand Down Expand Up @@ -58,8 +59,7 @@ def canEdit(self, user):
"""
Checks if a user can edit this UserFilter.
"""

return self.system.isAdmin(user)
return utils.is_admin_for(user, self.system)

def get_absolute_url(self):
from django.core.urlresolvers import reverse
Expand Down
18 changes: 10 additions & 8 deletions filters/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@
FilterTestUserForm
import filters.forest as forest

import json

from votes.models import VotingSystem

from jay.utils import priviliged
from jay.utils import priviliged, is_elevated, get_all_systems, \
get_user_details

import json

FILTER_FOREST_TEMPLATE = "filters/filter_forest.html"
FILTER_EDIT_TEMPLATE = "filters/filter_edit.html"
Expand All @@ -27,7 +28,7 @@
@priviliged
def Forest(request, alert_type=None, alert_head=None, alert_text=None):
# if the user does not have enough priviliges, throw an exception
if not request.user.profile.isElevated():
if not is_elevated(request.user):
raise PermissionDenied

# build a new context
Expand All @@ -40,7 +41,7 @@ def Forest(request, alert_type=None, alert_head=None, alert_text=None):
{'url': reverse('filters:forest'), 'text': 'Filters', 'active': True})
ctx['breadcrumbs'] = bc

(admin_systems, other_systems) = request.user.profile.getSystems()
(admin_systems, other_systems) = get_all_systems(request.user)

# give those to the view
ctx['admin_systems'] = admin_systems
Expand Down Expand Up @@ -79,7 +80,7 @@ def FilterNew(request):

# check if the user can edit it.
# if not, go back to the overview
if not system.isAdmin(request.user.profile):
if not system.isAdmin(request.user):
return Forest(request, alert_head="Creation failed",
alert_text="Nice try. You are not allowed to edit "
"this VotingSystem. ")
Expand Down Expand Up @@ -149,7 +150,7 @@ def FilterEdit(request, filter_id):
ctx["filter"] = filter

# check if the user can edit it
if not filter.canEdit(request.user.profile):
if not filter.canEdit(request.user):
raise PermissionDenied

# Set up the breadcrumbs
Expand Down Expand Up @@ -259,7 +260,8 @@ def FilterTestUser(request, filter_id):
form = FilterTestUserForm(request.POST)
if form.is_valid():
obj = form.cleaned_data["user"]
obj = User.objects.filter(username=obj)[0].profile.details
obj = json.dumps(get_user_details(
User.objects.filter(username=obj)[0]))
except Exception as e:
print(e)
pass
Expand Down
19 changes: 16 additions & 3 deletions jay/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,15 @@
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',

'django_forms_bootstrap',

'allauth',
'allauth.account',
'allauth.socialaccount',
'dreamjub.providers.oauth',

'filters',
'settings',
'users',
Expand Down Expand Up @@ -65,12 +73,14 @@
WSGI_APPLICATION = 'jay.wsgi.application'

# OpenJUB auth
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',
'users.ojub_auth.OjubBackend')
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
)

# Default after login redirect
# These are named URL routes
LOGIN_URL = "login"
LOGIN_URL = "accounts/dreamjub/login"
LOGOUT_URL = "logout"
LOGIN_REDIRECT_URL = "home"

Expand All @@ -91,3 +101,6 @@
)

STATIC_URL = '/static/'
SITE_ID = 1

ACCOUNT_EMAIL_VERIFICATION = "none"
3 changes: 1 addition & 2 deletions jay/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@
name="filter_help"),

# Authentication
url(r'^login/', auth_views.login, {'template_name': 'auth/login.html'},
name="login"),
url(r'^accounts/', include('allauth.urls'), name='login'),
url(r'^logout/', auth_views.logout,
{'template_name': 'auth/logout.html', 'next_page': 'home'},
name="logout"),
Expand Down
63 changes: 60 additions & 3 deletions jay/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,77 @@ def superadmin(handler):
""" Checks if a user is a super admin. """

def helper(request, *args, **kwargs):
if not request.user.profile.isSuperAdmin():
if not request.user.is_superuser:
raise PermissionDenied
return handler(request, *args, **kwargs)

return helper


def is_elevated(user):
if not user.is_superuser:
if not user.admin_set.count() > 0:
return False
return True


def priviliged(handler):
""" Checks that a user has elevated privileges. """
"""
Checks that a user has elevated priviliges.
"""

def helper(request, *args, **kwargs):
if not request.user.profile.isElevated():
if not is_elevated(request.user):
raise PermissionDenied

return handler(request, *args, **kwargs)

return helper


def get_user_details(user):
import json

try:
data = user.socialaccount_set.get(provider="dreamjub").extra_data
return data
except:
return {}


def is_admin_for(user, system):
"""
Checks if this user can administer a certain voting system.
"""
return system in get_administrated_systems(user)


def get_administrated_systems(user):
from settings.models import VotingSystem, Admin
"""
Returns all voting systems this user can administer.
"""
# if we are a superadmin we can manage all systems
if user.is_superuser:
return VotingSystem.objects.all()

# else return only the systems we are an admin for.
else:
return list(map(lambda x: x.system, Admin.objects.filter(
user=user)))


def get_all_systems(user):
from settings.models import VotingSystem
"""
Gets the editable filters for this user.
"""

# get all the voting systems for this user
admin_systems = get_administrated_systems(user)

# and all the other ones also
other_systems = list(
filter(lambda a: a not in admin_systems, VotingSystem.objects.all()))

return admin_systems, other_systems
12 changes: 8 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ Markdown==2.6.5
# For filters
PyExecJS==1.1.0

# For OpenJUB auth backend
requests==2.6.0

# For testing pep
pep8 >= 1.7.0
pep8 >= 1.7.0

# For reaching out to OpenJUB
requests==2.12.3

# For authing against dreamjub
django-allauth==0.29.0
django-allauth-dreamjub==0.1.3
2 changes: 1 addition & 1 deletion settings/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.contrib import admin

from jay.restricted import is_restricted_word
from users.models import Admin


class VotingSystem(models.Model):
Expand All @@ -27,5 +28,4 @@ def isAdmin(self, user):
"""
return user.isAdminFor(self)


admin.site.register(VotingSystem)
10 changes: 5 additions & 5 deletions settings/urls.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from django.conf.urls import url
from django.views.generic import TemplateView

from settings.views import superadmins, systems
from settings.views import systems

urlpatterns = [

# Superadmin management
url(r'^$', superadmins.settings, name="settings"),
url(r'^superadmins/add$', superadmins.superadmin_add, name="add"),
url(r'^superadmins/(?P<user_id>[\w-]+)/remove$',
superadmins.superadmin_remove, name="remove"),
# url(r'^$', superadmins.settings, name="settings"),
# url(r'^superadmins/add$', superadmins.superadmin_add, name="add"),
# url(r'^superadmins/(?P<user_id>[\w-]+)/remove$',
# superadmins.superadmin_remove, name="remove"),

# System management
url(r'^systems$', systems.systems, name='systems'),
Expand Down
50 changes: 0 additions & 50 deletions templates/auth/login.html

This file was deleted.

6 changes: 3 additions & 3 deletions templates/base/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@
{{ user.username }} <span class="caret"></span>
</a>
<ul class="dropdown-menu">
{% if user.profile.isSuperAdmin %}
{% if user.is_superuser %}
<li class="dropdown-header">Management</li>

<li role="presentation"><a href="{% url 'settings:systems' %}">Voting Systems</a></li>
<li role="presentation"><a href="{% url 'settings:settings' %}">Super Admins</a></li>
{# <li role="presentation"><a href="{% url 'settings:settings' %}">Super Admins</a></li> #}
{% endif %}

{% with admin_for=user.profile.getAdministratedSystems %}
Expand All @@ -70,7 +70,7 @@
<li role="presentation"><a href="{% url 'django.contrib.auth.views.logout' %}">Logout</a></li>
</ul>
{% else %}
<a href="{% url 'django.contrib.auth.views.login' %}">Login</a>
<a href="/accounts/dreamjub/login">Login</a>
{% endif %}
</li>
</ul>
Expand Down
4 changes: 2 additions & 2 deletions templates/vote/fragments/vote_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
<a href="{{ vote_url }}">
&nbsp;{{ vote.name }}

{% if vote|can_delete:request.user.profile %}
{% if vote|can_delete:request.user %}
<a href="{% url 'votes:delete' system_name=vote.system.machine_name vote_name=vote.machine_name %}">
<span class="pull-left glyphicon glyphicon-remove"></span>
</a>
{% endif %}

{% if vote|can_edit:request.user.profile %}
{% if vote|can_edit:request.user %}
<a href="{% url 'votes:edit' system_name=vote.system.machine_name vote_name=vote.machine_name %}">
<span class="pull-right glyphicon glyphicon-pencil"></span>
</a>
Expand Down
Loading

0 comments on commit bf81a03

Please sign in to comment.