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.http.response import Http404
from django.shortcuts import get_object_or_404, redirect, render 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 import Section
from c3nav.mapdata.models.base import EDITOR_FORM_MODELS 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(): def get_dimensions():
# todo calculate this # todo calculate this
return (400, 240) 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', 'c3nav.api',
'rest_framework', 'rest_framework',
'c3nav.mapdata', 'c3nav.mapdata',
'c3nav.access',
'c3nav.routing', 'c3nav.routing',
'c3nav.site', 'c3nav.site',
'c3nav.editor', 'c3nav.editor',
@ -154,7 +153,6 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'c3nav.access.middleware.AccessTokenMiddleware',
] ]
try: try:

View file

@ -7,14 +7,7 @@ from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse from django.urls import reverse
from django.utils import timezone 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.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 = { ctype_mapping = {
'yes': ('up', 'down'), 'yes': ('up', 'down'),

View file

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