Skip to content

Commit

Permalink
Switch authentication and permission management
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 and evaluated using a custom JavaScript bridge.

This caused problem, as Jay was able potentially able to intercept user
passwords and also depended on two legacy systems, the OpenJUB API and
the memory intensive JavaScript evaluation of filters

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.

Furthermore, we also migrate the evaluation of filters to use a
python-based evaluation, which no longer needs a JavaScript runtime on
the server side.

See also issue #26.
  • Loading branch information
kuboschek authored and tkw1536 committed Nov 1, 2017
1 parent 7788502 commit f0211b3
Show file tree
Hide file tree
Showing 34 changed files with 1,122 additions and 674 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ 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`
3. update `DREAMJUB_CLIENT_ID` and `DREAMJUB_CLIENT_SECRET` variables

## License

Jay is © 2015-17 Leonhard Kuboschek, Tom Wiesing & Contributors. Licensed under MIT license. See [LICENSE.md](LICENSE.md) for details.
15 changes: 15 additions & 0 deletions core/templatetags/userflags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django import template
from settings.models import VotingSystem
from jay import utils

register = template.Library()


@register.filter()
def getAdminSystems(user):
return VotingSystem.getAdminSystems(user)


@register.filter()
def isSuperAdmin(user):
return utils.is_superadmin(user)
4 changes: 3 additions & 1 deletion 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,7 +15,8 @@ def home(request):
systems = VotingSystem.objects.all()

if request.user.is_authenticated():
details = json.loads(request.user.profile.details)
details = get_user_details(request.user)

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

ctx["vote_list_title"] = "Your votes"
Expand Down
26 changes: 0 additions & 26 deletions filters/forest.js

This file was deleted.

79 changes: 0 additions & 79 deletions filters/forest.py

This file was deleted.

Empty file added filters/forest/__init__.py
Empty file.
242 changes: 242 additions & 0 deletions filters/forest/layouter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
from . import logic


def layouter(tree, obj):
op = tree["operation"]

# we have a constant or a filter, there is only one thing to render
if ((op in logic.op_list["OP_TRUE"]) or (
op in logic.op_list["OP_FALSE"]) or (
op in logic.op_list["OPS_FILTERS"])):
return layout_const(op, tree, obj)

# for a unary operation, we add a new node on top
if (op in logic.op_list["OPS_UNARY"]):
right = layouter(tree["right"], obj)
return layout_unary(op, right, tree, obj)

# for a binary operation, we need to merge two existing parts
if (op in logic.op_list["OPS_BINARY"]):
left = layouter(tree["left"], obj)
right = layouter(tree["right"], obj)
return layout_binary(op, left, right, tree, obj)

raise Exception("Unexpected operator during rendering")


def layout_const(op, tree, obj):
doesMatch = logic.matches(tree, obj)
is_filter = False

# if it is a filter, include key, value
if (op in logic.op_list["OPS_FILTERS"]):
is_filter = True

return {
'size': [1, 1], # height x width
'mainX': 0,
'out': logic.matches(tree, obj),
'grid': [[
{
'type': 'node',
'prop': {
'class': 'const',
'op': op,

'is_filter': is_filter,
'key': tree['key'] if 'key' in tree else '',
'value': tree['value'] if 'value' in tree else '',

'input': [],
'output': doesMatch
}
}
]]
}


def layout_unary(op, right, tree, obj):
# get some properties from the right.
rightSize = right['size']
rightGrid = right['grid']
rightOut = right['out']
rightX = right['mainX']

# check the value we should return
doesMatch = logic.matches(tree, obj)

# these are two new lines to render
nodeline = []
connline = []

# create them with a lot of empty space.
for i in range(rightSize[1]):
if i != rightX:
nodeline.append({
"type": "empty"
})

connline.append({
"type": "empty"
})
else:
# push the node on top
nodeline.append({
'type': 'node',
'prop': {
'op': op,
'input': [rightOut],
'output': doesMatch
}
})

# push a connection line
connline.append({
'type': 'conn',
'prop': {
'class': 'tree_connect_ver',
'active': [rightOut]
}
})

# add the connection line
rightGrid.insert(0, connline)

# and the nodeline
rightGrid.insert(0, nodeline)

# and assemble the thing to return
return {
'size': [rightSize[0] + 2, right['size'][1]],
'mainX': rightX,
'out': doesMatch,
'grid': rightGrid
}


def make_empty_line(size):
return [{"type": "empty"} for i in range(size)]


def layout_binary(op, left, right, tree, obj):
# get some properties from the left.
leftSize = left['size']
leftGrid = left['grid']
leftOut = left['out']
leftX = left['mainX']

# get some properties from the right.
rightSize = right['size']
rightGrid = right['grid']
rightOut = right['out']
rightX = right['mainX']

# the new size
newWidth = leftSize[1] + rightSize[1] + 1
newHeight = max(leftSize[0], rightSize[0]) + 2

# check the value we should return
doesMatch = logic.matches(tree, obj)

# these are two new lines to render
nodeline = []
connline = []

# create the new top lines
for i in range(newWidth):
if (i != leftSize[1]):
nodeline.append({
'type': 'empty'
})

# for the left x, we need to connect towards the right
if (i == leftX):
connline.append({
'type': 'conn',
'prop': {
'active': [leftOut],
'class': 'tree_connect_bot_right'
}
})
elif i < leftX:
connline.append({
'type': 'empty'
})
elif (i <= leftSize[1]):
connline.append({
'type': 'conn',
'prop': {
'active': [leftOut],
'class': 'tree_connect_hor'
}
})
elif (i <= leftSize[1] + rightX):
connline.append({
'type': 'conn',
'prop': {
'active': [rightOut],
'class': 'tree_connect_hor'
}
})
elif (i == leftSize[1] + rightX + 1):
connline.append({
'type': 'conn',
'prop': {
'active': [rightOut],
'class': 'tree_connect_bot_left'
}
})
else:
connline.append({
'type': 'empty'
})
else:
# push the node on top
nodeline.append({
'type': 'node',
'prop': {
'class': 'binary',
'op': op,
'input': [leftOut, rightOut],
'output': doesMatch
}
})

# push the connection line l / r
connline.append({
'type': 'conn',
'prop': {
'active': [leftOut, rightOut],
'class': 'tree_connect_top_lr'
}
})

# create a new grid
newGrid = []

# push the top two lines
newGrid.append(nodeline)
newGrid.append(connline)

for i in range(newHeight - 2):
newGrid.append(
[] +

(leftGrid[i] if len(leftGrid) > i else make_empty_line(
leftSize[1])) +

[{
"type": "empty"
}] +

(rightGrid[i] if len(rightGrid) > i else make_empty_line(
rightSize[1]))
)

# and assemble the thing to return
return {
'size': [newHeight, newWidth],
'mainX': leftSize[1],
'out': doesMatch,
'grid': newGrid
}
Loading

0 comments on commit f0211b3

Please sign in to comment.