...
 
Commits (12)
......@@ -39,6 +39,7 @@ import os
import sys
import logging
import radiusd # Module magique freeradius (radiusd.py is dummy)
import json
from django.core.wsgi import get_wsgi_application
from django.db.models import Q
......@@ -68,6 +69,7 @@ RADIUS_POLICY = options.radius_general_policy
#: Serveur radius de test (pas la prod)
TEST_SERVER = bool(os.getenv('DBG_FREERADIUS', False))
LEGACY_WIFI_PASSWORDS = json.load(open('/etc/freeradius/3.0/wifi_passwords', 'r'))
# Logging
class RadiusdHandler(logging.Handler):
......@@ -122,7 +124,7 @@ def radius_event(fun):
return fun(data)
except Exception as err:
logger.error('Failed %r on data %r' % (err, auth_data))
raise
return radiusd.RLM_MODULE_FAIL
return new_f
......@@ -156,6 +158,34 @@ def authorize(data):
user = data.get('User-Name', '').decode('utf-8', errors='replace')
user = user.split('@', 1)[0]
mac = data.get('Calling-Station-Id', '')
#### Legacy, anciens login/mdp des machines
if user in LEGACY_WIFI_PASSWORDS:
logger.info(u"Legacy auth for login %s" % user.encode('utf-8'))
interface = Interface.objects.filter(domain__name=user, mac_address=mac).first()
if not interface:
logger.info(u"Rejet, Interface introuvable, mac et user login differents")
return radiusd.RLM_MODULE_REJECT
if not interface.is_active:
logger.info(u"Rejet, interface desactivee")
return radiusd.RLM_MODULE_REJECT
user_object = interface.machine.user
if not user_object.has_access():
logger.info(u"Adherent non connecte/cotisant")
return radiusd.RLM_MODULE_REJECT
return (
radiusd.RLM_MODULE_UPDATED,
(),
(
(str("Cleartext-Password"), str(LEGACY_WIFI_PASSWORDS[user])),
),
)
result, log, password = check_user_machine_and_register(
nas_type,
user,
......
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2018-08-09 14:52
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('machines', '0093_auto_20180807_1115'),
]
operations = [
migrations.AlterField(
model_name='txt',
name='field1',
field=models.CharField(blank=True, max_length=255),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2018-08-11 02:20
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('machines', '0094_auto_20180809_1652'),
]
operations = [
migrations.AlterField(
model_name='iptype',
name='reverse_v4',
field=models.BooleanField(default=False, help_text='Enable reverse DNS for IPv4'),
),
migrations.AlterField(
model_name='iptype',
name='reverse_v6',
field=models.BooleanField(default=False, help_text='Enable reverse DNS for IPv6'),
),
]
......@@ -786,7 +786,7 @@ class Txt(RevMixin, AclMixin, models.Model):
PRETTY_NAME = "Enregistrement TXT"
zone = models.ForeignKey('Extension', on_delete=models.PROTECT)
field1 = models.CharField(max_length=255)
field1 = models.CharField(max_length=255, blank=True)
field2 = models.TextField(max_length=2047)
class Meta:
......
......@@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<th></th>
</tr>
</thead>
{% for txt in text_list %}
{% for txt in txt_list %}
<tr>
<td>{{ txt.zone }}</td>
<td>{{ txt.dns_entry }}</td>
......
......@@ -110,3 +110,6 @@ GID_RANGES = {
# Some Django apps you want to add in you local project
OPTIONNAL_APPS = ()
CAPTIVE_IP_RANGE = "10.51.0.0/16"
{% extends "users/sidebar.html" %}
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% load bootstrap3 %}
{% load static %}
{% block title %}Machine non reconnue{% endblock %}
{% block content %}
<div class="alert alert-success">
<h4 class="alert-heading">Création d'une machine fillaire</h4>
<p>Pour créer une machine fillaire, connectez vous puis débranchez et rebranchez votre cable Ethernet</p>
</div>
{% bootstrap_form_errors loginform %}
<form class="form" method="post">
{% csrf_token %}
{% bootstrap_form loginform %}
<button type="submit" class="btn btn-success">Login</button>
</form>
{% if success %}
Votre Machine a été enregistré avec succès, débranchez votre cable, attendez quelques secondes puis rebranchez le, vous devriez alors avoir internet ! (si vous êtes à jour de cotisation).
{% endif %}
{% endblock %}
......@@ -48,7 +48,7 @@ from django.conf.urls import include, url
from django.contrib import admin
from django.contrib.auth import views as auth_views
from .views import index, about_page, contact_page
from .views import index, about_page, contact_page, portail_login
handler500 = 're2o.views.handler500'
handler404 = 're2o.views.handler404'
......@@ -58,6 +58,7 @@ urlpatterns = [
url(r'^about/$', about_page, name='about'),
url(r'^contact/$', contact_page, name='contact'),
url('^logout/', auth_views.logout, {'next_page': '/'}),
url(r'^portail_login/$', portail_login, name='portail_login'),
url('^', include('django.contrib.auth.urls')),
url(r'^i18n/', include('django.conf.urls.i18n')),
url(r'^admin/', include(admin.site.urls)),
......
......@@ -34,6 +34,11 @@ from django.template.context_processors import csrf
from django.conf import settings
from django.utils.translation import ugettext as _
from django.views.decorators.cache import cache_page
from django.contrib.auth.forms import AuthenticationForm
from django.contrib import messages
import subprocess
import ipaddress
from preferences.models import (
Service,
......@@ -41,9 +46,10 @@ from preferences.models import (
AssoOption,
HomeOption
)
from machines.models import Nas
from .contributors import CONTRIBUTORS
from re2o.settings import CAPTIVE_IP_RANGE
def form(ctx, template, request):
"""Form générique, raccourci importé par les fonctions views du site"""
......@@ -69,6 +75,60 @@ def index(request):
'asso_name': asso_name
}, 're2o/index.html', request)
def get_ip(request):
"""Returns the IP of the request, accounting for the possibility of being
behind a proxy.
"""
ip = request.META.get("HTTP_X_FORWARDED_FOR", None)
if ip:
# X_FORWARDED_FOR returns client1, proxy1, proxy2,...
ip = ip.split(", ")[0]
else:
ip = request.META.get("REMOTE_ADDR", "")
return ip
def apply(cmd):
return subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
def mac_from_ip(ip):
cmd = ['/usr/sbin/arp','-na',ip]
p = apply(cmd)
output, errors = p.communicate()
if output is not None :
mac_addr = output.decode().split()[3]
return str(mac_addr)
else:
return None
def portail_login(request):
""" Check for authentication. If success, register the current machine for the user."""
login_form = AuthenticationForm(data=request.POST or None)
success = False
if login_form.is_valid():
remote_ip = get_ip(request)
if ipaddress.ip_address(remote_ip) in ipaddress.ip_network(CAPTIVE_IP_RANGE):
mac_addr = mac_from_ip(remote_ip)
if mac_addr:
nas_type = Nas.objects.get(name="Switches")
result, reason = login_form.user_cache.autoregister_machine(mac_addr, nas_type)
if result:
success=True
else:
messages.error(request, "Erreur dans l'enregistrement de la machine : %s" % reason, '')
else:
messages.error(request, "Erreur dans la récupération de la MAC")
else:
messages.error(request, "Merci de vous connecter en filaire pour enregistrer une machine")
return form(
{
'loginform': login_form,
'success' : success
},
're2o/portail_login.html',
request
)
def about_page(request):
""" The view for the about page.
......
......@@ -620,6 +620,9 @@ class EMailAddressForm(FormRevMixin, ModelForm):
super(EMailAddressForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['local_part'].label = "Local part of the email"
self.fields['local_part'].help_text = "Can't contain @"
def clean_local_part(self):
return self.cleaned_data.get('local_part').lower()
def clean_local_part(self):
return self.cleaned_data.get('local_part').lower()
......@@ -646,6 +649,12 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
else:
raise forms.ValidationError("Vous ne pouvez pas utiliser une addresse {}".format(OptionalUser.objects.first().local_email_domain))
def clean_email(self):
if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get('email'):
return self.cleaned_data.get('email').lower()
else:
raise forms.ValidationError("Vous ne pouvez pas utiliser une addresse {}".format(OptionalUser.objects.first().local_email_domain))
class Meta:
model = User
fields = ['email','local_email_enabled', 'local_email_redirect']
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2018-08-11 02:20
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0074_auto_20180810_2104'),
]
operations = [
migrations.AlterField(
model_name='adherent',
name='gpg_fingerprint',
field=models.CharField(blank=True, max_length=40, null=True, validators=[django.core.validators.RegexValidator('^[0-9A-F]{40}$', message='Une fingerprint GPG doit contenir 40 caractères hexadécimaux')]),
),
migrations.AlterField(
model_name='user',
name='email',
field=models.EmailField(max_length=254, unique=True),
),
]
......@@ -104,7 +104,6 @@ def linux_user_validator(login):
params={'label': login},
)
def get_fresh_user_uid():
""" Renvoie le plus petit uid non pris. Fonction très paresseuse """
uids = list(range(
......@@ -1132,8 +1131,7 @@ def user_post_save(**kwargs):
Synchronise le ldap"""
is_created = kwargs['created']
user = kwargs['instance']
EMailAddress.objects.get_or_create(
local_part=user.pseudo.lower(), user=user)
EMailAddress.objects.get_or_create(local_part=user.pseudo.lower(), user=user)
if is_created:
user.notif_inscription()
user.state_sync()
......
......@@ -393,6 +393,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</div>
</div>
</div>
{% can_create whitelist %}
<div class="panel panel-default">
<div class="panel-heading clearfix profil" data-parent="#accordion" data-toggle="collapse" data-target="#collapse6">
<h3 class="panel-title pull-left">
......@@ -418,6 +419,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</div>
</div>
</div>
{% acl_end %}
<div class="panel panel-default">
<div class="panel-heading clearfix profil" data-parent="#accordion" data-toggle="collapse" data-target="#collapse7">
<h3 class="panel-title pull-left">
......
......@@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Créer un club/association
</a>
{% acl_end %}
{% can_create Adherent %}
{% can_create User %}
<a class="list-group-item list-group-item-success" href="{% url "users:new-user" %}">
<i class="fa fa-user-plus"></i>
Créer un adhérent
......