Commit fc376834 authored by Daniel STAN's avatar Daniel STAN

nouvelle app de validation d'actions (warn upload)

Cette application a pour but d'autoriser certaines actions directement par mail,
avec une url de retour.

Ce premier commit implémente l'enregistrement de nouvelles actions, ainsi
qu'un premier type d'action: la confirmation d'un upload récent.
Lorsqu'un utilisateur est bridé pour upload, il doit se rendre sur cette
page web pour confirmer qu'il est conscient du problème, et ainsi reprogrammer
son débridage (au bout de 24h)
parent 9e213a25
#~*~ coding: utf-8 ~*~
from django.contrib import admin
from django import forms
import models
admin.site.register(models.ConfirmAction)
# -*- coding: utf-8 -*-
import random
import string
import datetime
from django.db import models
from django.core.urlresolvers import reverse
def _generate_secret():
"""Génère un mot de passe aléatoire"""
random.seed(datetime.datetime.now().microsecond)
chars = string.letters + string.digits + '=+*.,$'
length = 64
return u''.join([random.choice(chars) for _ in xrange(length)])
class ConfirmAction(models.Model):
"""Une action envoyée par mail à un utilisateur, et qui demande
confirmation"""
view_name = models.CharField(max_length=255)
secret = models.CharField(max_length=64, default=_generate_secret)
created = models.DateTimeField(auto_now=True)
triggered = models.DateTimeField(null=True, blank=True)
data = models.TextField(blank=True)
def get_absolute_url(self):
"""Il s'agit de l'url que doit charger l'utilisateur pour voir la page"""
return reverse('validation:' + self.view_name, args=[str(self.pk), str(self.secret)])
def __unicode__(self):
data = self.data
if len(data) > 64:
data = data[0:63] + "…"
return u"%s (%s)" % (self.view_name, data)
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
from django.conf.urls import include, patterns, url
import views
urlpatterns = patterns('',
# Ceci est la vue appelée par les scripts désireux d'enregister une
# nouvelle action
url('^register/(.*)/$', views.register, name='register'),
# Fonctions de callback (le name doit correspondre à celui fourni
# dans ConfirmAction.view_name
url('^upload/(\d+)/(.*)/$', views.upload, name='upload'),
# TODO
# faire de même pour:
# * chbre invalide
# * compte inactif
# * machine inutilisée depuis n jours (cf vue "keep" de l'app "machines")
)
# -*- coding: utf-8 -*-
# Create your views here.
import datetime
import json
from django.shortcuts import render
from django.shortcuts import get_object_or_404
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse, HttpResponseForbidden
from models import ConfirmAction
import lc_ldap.shortcuts
from gestion import secrets_new as secrets
use_ldap_admin = lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5,
constructor=lc_ldap.shortcuts.lc_ldap_admin)
@use_ldap_admin
def upload(request, pk, secret, ldap):
"""
Validation d'une déconnexion. Reprogramme la fin d'une déconnexion
pour dans 24h.
"""
task = get_object_or_404(ConfirmAction, pk=pk, view_name='upload',
secret=secret)
data = json.loads(task.data)
with ldap.search(data['dn'], mode='rw')[0] as adh:
bl = adh['blacklist'][data['blid']].value
if bl['fin'] == '-' and 'confirm' in request.POST:
bl['fin'] = bl['debut'] + 24*60*60
adh.history_add(u'validate', u'blacklist_upload (fin)')
adh.save()
task.triggered = datetime.datetime.now()
task.save()
if bl['fin'] != '-':
fin = datetime.datetime.fromtimestamp(bl['fin'])
return render(request, 'validation/upload_done.html',
{'fin': fin})
else:
return render(request, 'validation/upload.html')
@csrf_exempt
def register(request, view_name):
"""Enregistre une action à effectuer. Les données d'exécution sont dans
request.POST['data'].
Pour être sûr que l'appelant avait le droit de faire cet appel, on vérifie
le secret partagé dans request.POST['shared_secret'].
Affiche en réponse une url de callback"""
if request.POST.get('shared_secret', '') != secrets.get('validation'):
return HttpResponseForbidden()
task = ConfirmAction()
task.data = request.POST.get('data', '')
task.view_name = view_name
task.save()
return HttpResponse(task.get_absolute_url(), content_type="text/plain")
......@@ -284,7 +284,13 @@ INTRANET_APPS = (
'category': 'Administration',
'label': 'Mes factures',
'test': lambda u: u.groups.filter(name='crans_nounou'),
}
},
{
'name': 'validation',
'category': 'Administration',
'label': 'Validation',
'test': lambda _: False, # Personne n'a besoin de voir cette appli
},
)
INTRANET_APPS = tuple([ i for i in INTRANET_APPS if i['name'] not in
......
{% extends "template.html" %}
{% block content %}
<h3>Bridage pour upload</h3>
<p>
Ton débit a été momentanément réduit en raison d'un envoi de données trop
important. Afin d'éviter que de tels dépassements ne se reproduisent, merci
de confirmer que les programmes ayant généré ce trafic ont été identifiés et
sont désormais correctement configurés, ou à défaut, désactivés.
<a href="https://wiki.crans.org/VieCrans/AvertissementPourUpload">Davantage
d'information sur l'envoi de données est disponible sur le wiki.</a>
</p>
<form method="post">
{% csrf_token %}
<input type="submit" name="confirm" value="Oui, j'ai pris les mesures nécessaires." />
</form>
{% endblock %}
{% extends "template.html" %}
{% block content %}
<h3>Bridage pour upload</h3>
<p>
Durée avant la fin du bridage&nbsp;: {{ fin|timeuntil }}.
</p>
{% endblock %}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment