remove old access app and old cache and inclusion code

This commit is contained in:
Laura Klünder 2017-05-14 14:16:52 +02:00
parent c1d5c0ebef
commit 904e8f199c
31 changed files with 0 additions and 1212 deletions

View file

@ -1,66 +0,0 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from c3nav.access.models import AccessOperator, AccessToken, AccessTokenInstance, AccessUser
class AccessOperatorInline(admin.StackedInline):
model = AccessOperator
can_delete = False
class UserAdmin(BaseUserAdmin):
fieldsets = (
(None, {'fields': ('username', 'password', 'email')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
'groups', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
readonly_fields = ('last_login', 'date_joined',)
inlines = (AccessOperatorInline, )
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
class AccessTokenInline(admin.TabularInline):
model = AccessToken
show_change_link = True
readonly_fields = ('author', 'permissions', 'description', 'creation_date', 'activated', 'expires', 'expired')
def has_add_permission(self, request):
return False
@admin.register(AccessUser)
class AccessUserAdmin(admin.ModelAdmin):
inlines = (AccessTokenInline,)
list_display = ('user_url', 'creation_date', 'author', 'description')
fields = ('user_url', 'creation_date', 'author', 'description')
readonly_fields = ('creation_date', )
class AccessTokenInstanceInline(admin.TabularInline):
model = AccessTokenInstance
fields = ('secret', 'creation_date', 'expires', )
readonly_fields = ('secret', 'creation_date', 'expires', )
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
@admin.register(AccessToken)
class AccessTokenAdmin(admin.ModelAdmin):
inlines = (AccessTokenInstanceInline,)
list_display = ('__str__', 'user', 'permissions', 'author', 'creation_date', 'activated', 'expires', 'expired')
fields = ('user', 'permissions', 'author', 'creation_date', 'activated', 'expires', 'expired', 'description')
readonly_fields = ('user', 'creation_date', 'activated', 'expired', 'description')
def has_add_permission(self, request):
return False

View file

@ -1,27 +0,0 @@
from django.db.models import Q
from c3nav.mapdata.inclusion import get_maybe_invisible_areas_names
def can_access(request, item):
# todo implement this
return True
def filter_queryset_by_access(request, queryset, filter_location_inclusion=False):
# todo implement this
return queryset if request.c3nav_full_access else queryset.filter(public=True)
def filter_arealocations_by_access(request, queryset):
# todo implement this
if request.c3nav_full_access:
return queryset
return queryset.filter(Q(Q(public=True), ~Q(routing_inclusion='needs_permission')) |
Q(name__in=request.c3nav_access_list))
def get_visible_areas(request):
areas = [':full' if request.c3nav_full_access else ':base']
areas += [name for name in get_maybe_invisible_areas_names() if name in request.c3nav_access_list]
return areas

View file

@ -1,76 +0,0 @@
from django.forms import ModelForm, MultipleChoiceField
from django.utils.translation import ugettext_lazy as _
from c3nav.access.models import AccessToken, AccessUser
def get_permissions_field(request):
locations = AreaLocation.objects.filter(routing_inclusion='needs_permission').order_by('name')
has_operator = True
try:
request.user.operator
except:
has_operator = False
OPTIONS = []
can_full = False
if request.user.is_superuser:
can_full = True
elif has_operator:
can_award = request.user.operator.can_award_permissions.split(';')
can_full = ':full' in can_award
if not can_full:
locations = locations.filter(name__in=can_award)
else:
locations = []
if can_full:
OPTIONS.append((':full', _('Full Permissions')))
locationgroups = {}
locationgroups_count = {}
for location in locations:
for group in location.groups.all():
if group.location_id not in locationgroups:
locationgroups[group.location_id] = group
locationgroups_count[group.location_id] = 0
locationgroups_count[group.location_id] += 1
locationgroup_options = []
for location_id, group in locationgroups.items():
locationgroup_options.append((location_id, _('%(grouptitle)s (%(count)s nonpublic locations)') % {
'grouptitle': group.title,
'count': locationgroups_count[location_id]
}))
OPTIONS += sorted(locationgroup_options)
if can_full:
OPTIONS.append((':full', _('Full Permissions')))
OPTIONS += [(location.name, location.title) for location in locations]
return MultipleChoiceField(choices=OPTIONS, required=True)
class AccessTokenForm(ModelForm):
def __init__(self, *args, request, **kwargs):
super().__init__(*args, **kwargs)
self.fields['permissions'] = get_permissions_field(request)
class Meta:
model = AccessToken
fields = ['permissions', 'description', 'expires']
def clean_permissions(self):
data = self.cleaned_data['permissions']
if ':full' in data:
data = [':full']
data = ';'.join(data)
return data
class AccessUserForm(ModelForm):
class Meta:
model = AccessUser
fields = ['user_url', 'description']

View file

@ -1,57 +0,0 @@
import re
from datetime import timedelta
from django.conf import settings
from django.db import transaction
from django.db.models import Q
from django.utils import timezone
from c3nav.access.models import AccessTokenInstance
class AccessTokenMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.c3nav_access_instance = None
request.c3nav_access = None
request.c3nav_new_access = False
request.c3nav_full_access = settings.DEBUG
request.c3nav_access_list = ()
access_cookie = request.COOKIES.get('c3nav_access')
if access_cookie and re.match(r'^[0-9]+:[a-zA-Z0-9]+$', access_cookie):
pk, secret = access_cookie.split(':')
queryset = AccessTokenInstance.objects.filter(Q(access_token__id=int(pk), secret=secret),
Q(expires__isnull=True) | Q(expires__gt=timezone.now()),
Q(access_token__expired=False),
Q(access_token__expires__isnull=True) |
Q(access_token__expires__gt=timezone.now()))
access_instance = queryset.select_related('access_token').first()
if access_instance:
request.c3nav_access_instance = access_instance
request.c3nav_access = access_instance.access_token
request.c3nav_access.instances.filter(creation_date__lt=access_instance.creation_date).delete()
request.c3nav_full_access = request.c3nav_access.full_access
request.c3nav_access_list = request.c3nav_access.permissions_list
response = self.get_response(request)
if request.c3nav_access is not None:
with transaction.atomic():
cookie_value = request.c3nav_access.new_instance()
response.set_cookie('c3nav_access', cookie_value, expires=timezone.now() + timedelta(days=30))
if request.c3nav_new_access:
request.c3nav_access.activated = True
request.c3nav_access.save()
if request.c3nav_access_instance:
access_token = request.c3nav_access_instance.access_token
access_token.expired = True
access_token.save()
return response

View file

@ -1,83 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.4 on 2016-12-21 17:21
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='AccessOperator',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('description', models.TextField(blank=True, null=True, verbose_name='description')),
('can_award_permissions', models.CharField(max_length=2048, verbose_name='can award permissions')),
('access_from', models.DateTimeField(blank=True, null=True, verbose_name='has access from')),
('access_until', models.DateTimeField(blank=True, null=True, verbose_name='has access until')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='operator', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name_plural': 'Access Operator',
'verbose_name': 'Access Operator',
},
),
migrations.CreateModel(
name='AccessToken',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('permissions', models.CharField(max_length=2048, verbose_name='permissions')),
('description', models.CharField(max_length=200, verbose_name='description')),
('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
('expires', models.DateTimeField(blank=True, null=True)),
('expired', models.BooleanField(default=False, verbose_name='is expired')),
('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='creator')),
],
options={
'verbose_name_plural': 'Access Tokens',
'verbose_name': 'Access Token',
},
),
migrations.CreateModel(
name='AccessTokenInstance',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('secret', models.CharField(max_length=42, verbose_name='access secret')),
('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
('expires', models.DateTimeField(null=True)),
('access_token', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='instances', to='access.AccessToken', verbose_name='Access Token')),
],
options={
'verbose_name_plural': 'Access Tokens Instance',
'verbose_name': 'Access Token Instance',
},
),
migrations.CreateModel(
name='AccessUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('user_url', models.CharField(help_text='Usually an URL to a profile somewhere', max_length=200, unique=True, verbose_name='access name')),
('description', models.TextField(blank=True, max_length=200, null=True, verbose_name='description')),
('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='access.AccessOperator', verbose_name='creator')),
],
options={
'verbose_name_plural': 'Access Users',
'verbose_name': 'Access User',
},
),
migrations.AddField(
model_name='accesstoken',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tokens', to='access.AccessUser', verbose_name='Access User'),
),
]

View file

@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.4 on 2016-12-21 17:39
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='accesstoken',
name='activated',
field=models.BooleanField(default=False, verbose_name='activated'),
),
migrations.AddField(
model_name='accesstoken',
name='secret',
field=models.CharField(default='', max_length=42, verbose_name='activation secret'),
preserve_default=False,
),
]

View file

@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.4 on 2016-12-21 23:11
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0002_auto_20161221_1739'),
]
operations = [
migrations.AlterField(
model_name='accessoperator',
name='description',
field=models.TextField(blank=True, default='', verbose_name='description'),
preserve_default=False,
),
migrations.AlterField(
model_name='accessuser',
name='description',
field=models.TextField(blank=True, default='', max_length=200, verbose_name='description'),
preserve_default=False,
),
]

View file

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.4 on 2016-12-23 22:25
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('access', '0003_auto_20161221_2311'),
]
operations = [
migrations.AlterModelOptions(
name='accesstokeninstance',
options={'verbose_name': 'Access Token Instance', 'verbose_name_plural': 'Access Token Instances'},
),
]

View file

@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.4 on 2016-12-25 10:18
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('access', '0004_auto_20161223_2225'),
]
operations = [
migrations.AlterField(
model_name='accessuser',
name='author',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='creator'),
),
]

View file

@ -1,126 +0,0 @@
import string
from datetime import timedelta
from django.contrib.auth.models import User
from django.db import models, transaction
from django.db.models import Q
from django.urls import reverse
from django.utils import timezone
from django.utils.crypto import get_random_string
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
class AccessOperator(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='operator')
description = models.TextField(_('description'), blank=True)
can_award_permissions = models.CharField(_('can award permissions'), max_length=2048)
access_from = models.DateTimeField(_('has access from'), null=True, blank=True)
access_until = models.DateTimeField(_('has access until'), null=True, blank=True)
class Meta:
verbose_name = _('Access Operator')
verbose_name_plural = _('Access Operator')
def __str__(self):
return str(self.user)
class AccessUser(models.Model):
user_url = models.CharField(_('access name'), unique=True, max_length=200,
help_text=_('Usually an URL to a profile somewhere'))
author = models.ForeignKey(User, on_delete=models.PROTECT, null=True, blank=True,
verbose_name=_('creator'))
description = models.TextField(_('description'), max_length=200, blank=True)
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
class Meta:
verbose_name = _('Access User')
verbose_name_plural = _('Access Users')
@property
def valid_tokens(self):
return self.tokens.filter(Q(expired=False) | Q(expires__isnull=False, expires__lt=timezone.now()))
def new_token(self, **kwargs):
kwargs['secret'] = AccessToken.create_secret()
return self.tokens.create(**kwargs)
def __str__(self):
return self.user_url
class AccessToken(models.Model):
user = models.ForeignKey(AccessUser, on_delete=models.CASCADE, related_name='tokens',
verbose_name=_('Access User'))
author = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name=_('creator'), null=True, blank=True)
permissions = models.CharField(_('permissions'), max_length=2048)
description = models.CharField(_('description'), max_length=200)
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
expires = models.DateTimeField(null=True, blank=True)
expired = models.BooleanField(_('is expired'), default=False)
activated = models.BooleanField(_('activated'), default=False)
secret = models.CharField(_('activation secret'), max_length=42)
class Meta:
verbose_name = _('Access Token')
verbose_name_plural = _('Access Tokens')
@cached_property
def permissions_list(self):
return self.permissions.split(';')
@cached_property
def permissions_list_objects(self):
return AreaLocation.objects.filter(name__in=self.permissions_list)
@cached_property
def full_access(self):
return ':full' in self.permissions_list
@property
def is_expired(self):
return self.expired or (self.expires is not None and self.expires < timezone.now())
@property
def activation_url(self):
if self.activated:
return None
return reverse('access.activate', kwargs={'pk': self.pk, 'secret': self.secret})
@property
def qr_url(self):
if self.activated:
return None
return reverse('access.qr', kwargs={'pk': self.pk, 'secret': self.secret})
@staticmethod
def create_secret():
return get_random_string(42, string.ascii_letters + string.digits)
def new_instance(self):
with transaction.atomic():
for instance in self.instances.filter(expires__isnull=True):
instance.expires = timezone.now()+timedelta(seconds=5)
instance.save()
self.instances.filter(expires__isnull=False, expires__lt=timezone.now()).delete()
secret = self.create_secret()
self.instances.create(secret=secret)
return '%d:%s' % (self.pk, secret)
def __str__(self):
return '%s #%d' % (_('Access Token'), self.id)
class AccessTokenInstance(models.Model):
access_token = models.ForeignKey(AccessToken, on_delete=models.CASCADE, related_name='instances',
verbose_name=_('Access Token'))
secret = models.CharField(_('access secret'), max_length=42)
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
expires = models.DateTimeField(null=True)
class Meta:
verbose_name = _('Access Token Instance')
verbose_name_plural = _('Access Token Instances')

View file

@ -1,44 +0,0 @@
body, .btn {
font-size:16px;
}
h1 {
margin-bottom:20px;
}
.login .container {
max-width:420px;
}
.pager .middle {
top:3px;
position:relative;
}
footer {
text-align:center;
}
.languages {
margin-bottom:0;
height:2px;
}
.languages button {
text-transform:none;
font-weight:normal;
margin-top:0 !important;
border-width:0 !important;
padding:0;
}
.table .btn {
font-size:14px;
}
.table tbody tr td {
min-height: 29px;
line-height:29px;
}
.token img {
width:100%;
}

View file

@ -1,16 +0,0 @@
{% extends 'access/base.html' %}
{% load bootstrap3 %}
{% load i18n %}
{% block bodyclass %}login{% endblock %}
{% block content %}
{% if success %}
<div class="alert alert-success">
<strong>{% trans 'Your access token was installed on your device!' %}</strong>
</div>
{% else %}
{% include 'access/fragment_token.html' %}
{% endif %}
{% endblock %}

View file

@ -1,33 +0,0 @@
{% load static %}
{% load compress %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>c3nav control panel</title>
{% compress css %}
<link href="{% static 'bootstrap/css/bootstrap.css' %}" rel="stylesheet">
<link href="{% static 'access/css/c3nav-access.css' %}" rel="stylesheet">
{% endcompress %}
</head>
<body class="{% block bodyclass %}{% endblock %}">
<div class="container" id="main">
<h1>c3nav access control</h1>
{% block nav %}
{% endblock %}
{% block content %}
{% endblock %}
{% include 'site/footer.html' %}
</div>
{% compress js %}
<script type="text/javascript" src="{% static 'jquery/jquery.js' %}"></script>
{% endcompress %}
</body>
</html>

View file

@ -1,17 +0,0 @@
{% load i18n %}
<form action="{{ token.activation_url }}" method="POST">
{% csrf_token %}
<div class="token">
<p>
<img src="{{ token.qr_url }}">
</p>
<p><a href="{{ token.activation_url }}">{% trans 'Link to this token / in this QR code' %}</a></p>
<p>
<button type="submit" class="btn btn-default btn-block">{% trans 'Activate token on this device' %}</button>
</p>
<p>
{% blocktrans %}This link QR Code / Link only works once. If you want access on multiple devices, you need multiple tokens.{% endblocktrans %}
</p>
</div>
</form>

View file

@ -1,10 +0,0 @@
{% extends 'access/base.html' %}
{% load i18n %}
{% block nav %}
<ul class="nav nav-pills">
<li class="active"><a href="{% url 'access.users' %}">{% trans 'Users' %}</a></li>
<li><a href="{% url 'access.logout' %}">{% trans 'Log out' %}</a></li>
</ul>
{% endblock %}

View file

@ -1,24 +0,0 @@
{% extends 'access/base.html' %}
{% load bootstrap3 %}
{% load i18n %}
{% block bodyclass %}login{% endblock %}
{% block content %}
<form method="POST">
{% csrf_token %}
<fieldset>
<legend>{% trans 'Log in' %}</legend>
{% bootstrap_form form %}
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block btn-lg">{% trans 'Log in' %}</button>
</div>
</fieldset>
<p>{% blocktrans %}This is the non-public backend for creation of access tokens.{% endblocktrans %}</p>
<p><a class="btn btn-sm btn-default btn-block" href="{% url 'access.prove' %}">
{% blocktrans %}Prove that you should have access{% endblocktrans %}
</a></p>
</form>
{% endblock %}

View file

@ -1,83 +0,0 @@
{% extends 'access/loggedin_base.html' %}
{% load bootstrap3 %}
{% load i18n %}
{% block content %}
<h3>Access Tokens <small>{{ user.user_url }}</small></h3>
<p>{{ user.description }}</p>
<form method="POST">
<div class="panel panel-default">
<div class="panel-body">
<h4>{% trans 'Add new access token' %}</h4>
<div class="row">
{% csrf_token %}
{% bootstrap_form new_token_form form_group_class='form-group col-md-3' %}
<div class="form-group col-md-3">
<label class="control-label">&nbsp;</label>
<button type="submit" name="create" class="btn btn-primary btn-block btn-sm">{% trans 'Add' %}</button>
</div>
</div>
</div>
</div>
</form>
<form method="POST">
{% csrf_token %}
<table class="table">
<thead>
<tr>
<th>{% trans 'ID' %}</th>
<th>{% trans 'Description' %}</th>
<th>{% trans 'Author' %}</th>
<th>{% trans 'Permissions' %}</th>
<th>{% trans 'Creation Date' %}</th>
<th>{% trans 'Expiration' %}</th>
<th>{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
{% for token in tokens %}
<tr>
<td>{{ token.id }}</td>
<td>{{ token.description }}</td>
<td>{% if token.author %}{{ token.author }}{% endif %}</td>
<td>
{% if token.full_access %}
<span class="text-success">all</span>
{% else %}
{% for location in token.permissions_list_objects %}
{{ location.title }},
{% endfor %}
{% endif %}
</td>
<td>{{ token.creation_date|date:"SHORT_DATETIME_FORMAT" }}</td>
<td>
{% if token.is_expired %}
{% trans 'expired' %}
{% elif token.expires %}
{{ token.expires|date:"SHORT_DATETIME_FORMAT" }}
{% else %}
{% trans 'never' %}
{% endif %}
</td>
<td>
{% if not token.is_expired %}
<button class="btn btn-xs btn-danger" type="submit" name="expire" value="{{ token.id }}">{% trans 'Expire' %}</button>
{% if not token.activated %}
<a href="{% url 'access.user.token' user=user.id token=token.id %}" class="btn btn-xs btn-success" type="submit" name="activate" value="{{ token.id }}">{% trans 'Activate' %}</a>
{% endif %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
{% endblock %}

View file

@ -1,13 +0,0 @@
{% extends 'access/loggedin_base.html' %}
{% load bootstrap3 %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col-md-4 col-md-offset-4">
{% include 'access/fragment_token.html' %}
</div>
</div>
<p><a href="{% url 'access.user' pk=user.id %}">{% trans 'back to user' %}</a></p>
{% endblock %}

View file

@ -1,70 +0,0 @@
{% extends 'access/loggedin_base.html' %}
{% load bootstrap3 %}
{% load i18n %}
{% block content %}
<h2>Users</h2>
<table class="table">
<thead>
<tr>
<th>{% trans 'ID' %}</th>
<th>{% trans 'Name' %}</th>
<th>{% trans 'Author' %}</th>
<th>{% trans 'Description' %}</th>
<th>{% trans 'Active Tokens' %}</th>
<th>{% trans 'Creation Date' %}</th>
<th>{% trans 'Details' %}</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.user_url }}</td>
<td>{% if user.author %}{{ user.author }}{% endif %}</td>
<td>{{ user.description }}</td>
<td>{{ user.valid_tokens.count }}</td>
<td>{{ user.creation_date|date:"SHORT_DATETIME_FORMAT" }}</td>
<td><a href="{% url 'access.user' pk=user.pk %}">{% trans 'Details' %}</td>
</tr>
{% endfor %}
</tbody>
</table>
<ul class="pager">
{% if users.has_previous %}
<li class="previous"><a href="{% url 'acces.users' page=users.previous_page_number %}">« {% trans 'previous' %}</a></li>
{% else %}
<li class="previous disabled"><a href="#">« {% trans 'previous' %}</a></li>
{% endif %}
<li class="middle">
{% blocktrans with number=users.number total=users.paginator.num_pages %}Page {{ number }} of {{ total }}{% endblocktrans %}
</li>
{% if users.has_next %}
<li class="next"><a href="{% url 'acces.users' page=users.next_page_number %}">{% trans 'next' %} »</a></li>
{% else %}
<li class="next disabled"><a href="#">{% trans 'next' %} »</a></li>
{% endif %}
</ul>
<form method="POST">
<div class="panel panel-default">
<div class="panel-body">
<h4>{% trans 'Add new access user' %}</h4>
<div class="row">
{% csrf_token %}
{% bootstrap_form new_user_form form_group_class='form-group col-md-4' %}
<div class="form-group col-md-4">
<label class="control-label">&nbsp;</label>
<button type="submit" name="create" class="btn btn-primary btn-block btn-sm">{% trans 'Add new User' %}</button>
</div>
</div>
</div>
</div>
</form>
{% endblock %}

View file

@ -1,16 +0,0 @@
from django.conf.urls import url
from django.contrib.auth import views as auth_views
from c3nav.access.views import activate_token, dashboard, show_user_token, token_qr, user_detail, user_list
urlpatterns = [
url(r'^$', dashboard, name='access.dashboard'),
url(r'^activate/(?P<pk>[0-9]+):(?P<secret>[a-zA-Z0-9]+)/$', activate_token, name='access.activate'),
url(r'^qr/(?P<pk>[0-9]+):(?P<secret>[a-zA-Z0-9]+).png$', token_qr, name='access.qr'),
url(r'^users/$', user_list, name='access.users'),
url(r'^users/(?P<page>[0-9]+)/$', user_list, name='access.users'),
url(r'^user/(?P<pk>[0-9]+)/$', user_detail, name='access.user'),
url(r'^user/(?P<user>[0-9]+)/(?P<token>[0-9]+)/$', show_user_token, name='access.user.token'),
url(r'^login/$', auth_views.login, {'template_name': 'access/login.html'}, name='access.login'),
url(r'^logout/$', auth_views.logout, name='access.logout'),
]

View file

@ -1,118 +0,0 @@
import qrcode
from django.contrib.auth.decorators import login_required
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from c3nav.access.forms import AccessTokenForm, AccessUserForm
from c3nav.access.models import AccessToken, AccessUser
@login_required(login_url='/access/login/')
def dashboard(request):
return redirect('access.users')
def activate_token(request, pk, secret):
token = get_object_or_404(AccessToken, expired=False, activated=False, id=pk, secret=secret)
if request.method == 'POST':
request.c3nav_access = token
request.c3nav_new_access = True
return render(request, 'access/activate.html', context={
'success': True,
})
return render(request, 'access/activate.html', context={
'token': token,
})
def token_qr(request, pk, secret):
get_object_or_404(AccessToken, expired=False, activated=False, id=pk, secret=secret)
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data(request.build_absolute_uri(reverse('access.activate', kwargs={'pk': pk, 'secret': secret})))
qr.make(fit=True)
response = HttpResponse(content_type='image/png')
qr.make_image().save(response, 'PNG')
return response
@login_required(login_url='/access/login/')
def user_list(request, page=1):
queryset = AccessUser.objects.all()
paginator = Paginator(queryset, 25)
try:
users = paginator.page(page)
except PageNotAnInteger:
return redirect('access.users')
except EmptyPage:
return redirect('access.users')
if request.method == 'POST':
new_user_form = AccessUserForm(data=request.POST)
if new_user_form.is_valid():
user = new_user_form.instance
user.author = request.user
user.save()
return redirect('access.user', pk=user.id)
else:
new_user_form = AccessUserForm()
return render(request, 'access/users.html', {
'users': users,
'new_user_form': new_user_form,
})
@login_required(login_url='/access/login/')
def user_detail(request, pk):
user = get_object_or_404(AccessUser, id=pk)
tokens = user.tokens.order_by('-creation_date')
if request.method == 'POST':
if 'expire' in request.POST:
token = get_object_or_404(AccessToken, user=user, id=request.POST['expire'])
token.expired = True
token.save()
return redirect('access.user', pk=user.id)
new_token_form = AccessTokenForm(data=request.POST, request=request)
if new_token_form.is_valid():
token = new_token_form.instance
token.user = user
token.secret = AccessToken.create_secret()
token.author = request.user
token.save()
return redirect('access.user.token', user=user.id, token=token.id)
else:
new_token_form = AccessTokenForm(request=request)
return render(request, 'access/user.html', {
'user': user,
'new_token_form': new_token_form,
'tokens': tokens,
})
@login_required(login_url='/access/login/')
def show_user_token(request, user, token):
user = get_object_or_404(AccessUser, id=user)
token = get_object_or_404(AccessToken, user=user, id=token)
return render(request, 'access/user_token.html', {
'user': user,
'token': token,
})

View file

@ -3,7 +3,6 @@ from django.core.exceptions import PermissionDenied
from django.http.response import Http404
from django.shortcuts import get_object_or_404, redirect, render
from c3nav.access.apply import can_access, filter_queryset_by_access
from c3nav.mapdata.models import Section
from c3nav.mapdata.models.base import EDITOR_FORM_MODELS

View file

@ -1,63 +0,0 @@
from collections import OrderedDict
from django.utils.translation import ugettext_lazy as _
def get_default_include_avoid():
include = set()
avoid = set()
locations = list(AreaLocation.objects.exclude(routing_inclusion='default'))
for location in locations:
if location.routing_inclusion != 'allow_avoid':
avoid.add(location.location_id)
return include, avoid
# Todo: add cache
def get_includables_avoidables(request):
includables = []
avoidables = []
locations = list(AreaLocation.objects.exclude(routing_inclusion='default'))
if request.c3nav_full_access:
includables.append((':nonpublic', _('non-public areas')))
avoidables.append((':public', _('public areas')))
for location in locations:
item = (location.location_id, location.title)
if location.location_id not in request.c3nav_access_list and not request.c3nav_full_access:
if location.routing_inclusion == 'needs_permission':
continue
if location.routing_inclusion == 'allow_avoid':
avoidables.append(item)
else:
includables.append(item)
return OrderedDict(includables), OrderedDict(avoidables)
def get_maybe_invisible_areas():
return AreaLocation.objects.exclude(routing_inclusion='default')
def get_maybe_invisible_areas_names():
return tuple(area.name for area in get_maybe_invisible_areas())
def parse_include_avoid(request, include_input, avoid_input):
includable, avoidable = get_includables_avoidables(request)
include_input = set(include_input) & set(includable)
avoid_input = set(avoid_input) & set(avoidable)
default_include, default_avoid = get_default_include_avoid()
include = set(default_include) | include_input
avoid = set(default_avoid) - include_input | avoid_input
return ':nonpublic' in includable, include, avoid

View file

@ -1,33 +0,0 @@
import os
import pickle
from contextlib import contextmanager
from django.conf import settings
from django.utils import timezone
last_mapdata_update_filename = os.path.join(settings.DATA_DIR, 'last_mapdata_update')
last_mapdata_update_decorator_depth = 0
def get_last_mapdata_update(default_now=False):
try:
with open(last_mapdata_update_filename, 'rb') as f:
return pickle.load(f)
except:
return timezone.now() if default_now else None
@contextmanager
def set_last_mapdata_update():
global last_mapdata_update_decorator_depth
if last_mapdata_update_decorator_depth == 0:
try:
os.remove(last_mapdata_update_filename)
except:
pass
last_mapdata_update_decorator_depth += 1
yield
last_mapdata_update_decorator_depth -= 1
if last_mapdata_update_decorator_depth == 0:
with open(last_mapdata_update_filename, 'wb') as f:
pickle.dump(timezone.now(), f)

View file

@ -1,97 +0,0 @@
from calendar import timegm
from collections import OrderedDict
from functools import wraps
from django.core.cache import cache
from django.db.models import Q
from django.utils.http import http_date
from rest_framework.response import Response as APIResponse
from rest_framework.views import APIView
from c3nav.mapdata.lastupdate import get_last_mapdata_update
def cache_result(cache_key, timeout=900):
def decorator(func):
@wraps(func)
def inner(*args, **kwargs):
last_update = get_last_mapdata_update()
if last_update is None:
return func(*args, **kwargs)
result = cache.get(cache_key)
if not result:
result = func(*args, **kwargs)
cache.set(cache_key, result, timeout)
return result
return inner
return decorator
def cache_mapdata_api_response(timeout=900):
def decorator(func):
@wraps(func)
def inner(self, request, *args, add_cache_key=None, **kwargs):
last_update = get_last_mapdata_update()
if last_update is None:
return func(self, request, *args, **kwargs)
cache_key = '__'.join((
'c3nav__mapdata__api',
last_update.isoformat(),
add_cache_key if add_cache_key is not None else '',
request.accepted_renderer.format if isinstance(self, APIView) else '',
request.path,
))
response = cache.get(cache_key)
if not response:
response = func(self, request, *args, **kwargs)
response['Last-Modifed'] = http_date(timegm(last_update.utctimetuple()))
if isinstance(response, APIResponse):
response = self.finalize_response(request, response, *args, **kwargs)
response.render()
if response.status_code < 400:
cache.set(cache_key, response, timeout)
return response
return inner
return decorator
class CachedReadOnlyViewSetMixin():
def _get_add_cache_key(self, request, add_cache_key=''):
cache_key = add_cache_key
return cache_key
def list(self, request, *args, **kwargs):
kwargs['add_cache_key'] = self._get_add_cache_key(request, kwargs.get('add_cache_key', ''))
return self._list(request, *args, **kwargs)
@cache_mapdata_api_response()
def _list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
def retrieve(self, request, *args, **kwargs):
kwargs['add_cache_key'] = self._get_add_cache_key(request, kwargs.get('add_cache_key', ''))
return self._retrieve(request, *args, **kwargs)
@cache_mapdata_api_response()
def _retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)
@cache_result('c3nav__mapdata__sections')
def get_sections_cached():
from c3nav.mapdata.models.section import Section
return OrderedDict((section.id, section) for section in Section.objects.all())
@cache_result('c3nav__mapdata__bssids')
def get_bssid_areas_cached():
from c3nav.mapdata.models import AreaLocation
bssids = {}
for area in AreaLocation.objects.filter(~Q(bssids='')):
for bssid in area.bssids.split('\n'):
bssids[bssid.strip()] = area.name
return bssids

View file

@ -1,37 +1,3 @@
import os
from django.conf import settings
from shapely.geometry import box
from shapely.ops import cascaded_union
from c3nav.mapdata.utils.cache import cache_result
@cache_result('c3nav__mapdata__dimensions')
def get_dimensions():
# todo calculate this
return (400, 240)
@cache_result('c3nav__mapdata__render_dimensions')
def get_render_dimensions():
width, height = get_dimensions()
return (width * settings.RENDER_SCALE, height * settings.RENDER_SCALE)
def get_render_path(filetype, level, mode, public):
return os.path.join(settings.RENDER_ROOT,
'%s%s-level-%s.%s' % (('public-' if public else ''), mode, level, filetype))
def get_public_private_area(level):
from c3nav.mapdata.models import AreaLocation
width, height = get_dimensions()
everything = box(0, 0, width, height)
needs_permission = [location.geometry
for location in AreaLocation.objects.filter(level=level,
routing_inclusion='needs_permission')]
public_area = level.public_geometries.areas_and_doors.difference(cascaded_union(needs_permission))
private_area = everything.difference(public_area)
return public_area, private_area

View file

@ -138,7 +138,6 @@ INSTALLED_APPS = [
'c3nav.api',
'rest_framework',
'c3nav.mapdata',
'c3nav.access',
'c3nav.routing',
'c3nav.site',
'c3nav.editor',
@ -154,7 +153,6 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'c3nav.access.middleware.AccessTokenMiddleware',
]
try:

View file

@ -7,14 +7,7 @@ from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils import timezone
from c3nav.access.apply import get_visible_areas
from c3nav.mapdata.inclusion import get_includables_avoidables, parse_include_avoid
from c3nav.mapdata.lastupdate import get_last_mapdata_update
from c3nav.mapdata.models.section import Section
from c3nav.mapdata.utils.cache import get_sections_cached
from c3nav.mapdata.utils.misc import get_dimensions, get_render_path
from c3nav.routing.exceptions import AlreadyThere, NoRouteFound, NotYetRoutable
from c3nav.routing.graph import Graph
ctype_mapping = {
'yes': ('up', 'down'),

View file

@ -1,13 +1,11 @@
from django.conf.urls import include, url
from django.contrib import admin
import c3nav.access.urls
import c3nav.api.urls
import c3nav.editor.urls
import c3nav.site.urls
urlpatterns = [
url(r'^access/', include(c3nav.access.urls)),
url(r'^editor/', include(c3nav.editor.urls)),
url(r'^api/', include(c3nav.api.urls, namespace='api')),
url(r'^admin/', admin.site.urls),