diff --git a/djnro/templates/front/index.html b/djnro/templates/front/index.html
index 18e63a07..c3890036 100644
--- a/djnro/templates/front/index.html
+++ b/djnro/templates/front/index.html
@@ -8,7 +8,7 @@
{% block bodyclass %}home{% endblock %}
{% block content %}
diff --git a/edumanage/management/commands/clearcache.py b/edumanage/management/commands/clearcache.py
new file mode 100644
index 00000000..c6739fbe
--- /dev/null
+++ b/edumanage/management/commands/clearcache.py
@@ -0,0 +1,9 @@
+# "edumanage/management/commands/clearcache.py"
+
+from django.core.management.base import BaseCommand
+from django.core.cache import cache
+
+class Command(BaseCommand):
+ def handle(self, *args, **kwargs):
+ cache.clear()
+ self.stdout.write('Cache is cleared\n')
\ No newline at end of file
diff --git a/edumanage/management/commands/servdata.py b/edumanage/management/commands/servdata.py
index 3d642791..e5413901 100644
--- a/edumanage/management/commands/servdata.py
+++ b/edumanage/management/commands/servdata.py
@@ -74,6 +74,8 @@ def servdata():
if srv.name:
srv_dict['label'] = srv.name
srv_dict['secret'] = srv.secret
+ srv_dict['addr_type'] = srv.addr_type
+ srv_dict['proto'] = srv.proto
root['clients'].update({srv_id: srv_dict})
servers = hosts.filter(ertype__in=ERTYPE_ROLES.IDP)
@@ -92,6 +94,8 @@ def servdata():
srv_dict['label'] = srv.name
srv_dict['secret'] = srv.secret
srv_dict['status_server'] = bool(srv.status_server)
+ srv_dict['addr_type'] = srv.addr_type
+ srv_dict['proto'] = srv.proto
root['servers'].update({srv_id: srv_dict})
if insts:
diff --git a/edumanage/migrations/0100_stats_only.py b/edumanage/migrations/0100_stats_only.py
new file mode 100644
index 00000000..1db76460
--- /dev/null
+++ b/edumanage/migrations/0100_stats_only.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.29 on 2024-12-12 07:32
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('edumanage', '0011_edb21_server_schema'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='institution',
+ name='ertype',
+ field=models.PositiveIntegerField(choices=[(0, 'None (Stats Only)'), (1, 'IdP only'), (2, 'SP only'), (3, 'IdP and SP')], db_column='type'),
+ ),
+ migrations.AlterField(
+ model_name='instserver',
+ name='ertype',
+ field=models.PositiveIntegerField(choices=[(0, 'None (Stats Only)'), (1, 'IdP only'), (2, 'SP only'), (3, 'IdP and SP')], db_column='type'),
+ ),
+ ]
diff --git a/edumanage/migrations/0101_display_ordering.py b/edumanage/migrations/0101_display_ordering.py
new file mode 100644
index 00000000..7c113085
--- /dev/null
+++ b/edumanage/migrations/0101_display_ordering.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.29 on 2024-12-12 07:32
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('edumanage', '0100_stats_only'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='contact',
+ options={'ordering': ['name'], 'verbose_name': 'Contact', 'verbose_name_plural': 'Contacts'},
+ ),
+ migrations.AlterModelOptions(
+ name='institutioncontactpool',
+ options={'ordering': ['contact__name'], 'verbose_name': 'Instutution Contact (Pool)', 'verbose_name_plural': 'Instutution Contacts (Pool)'},
+ ),
+ migrations.AlterModelOptions(
+ name='instrealm',
+ options={'ordering': ['realm'], 'verbose_name': 'Institution Realm', 'verbose_name_plural': "Institutions' Realms"},
+ ),
+ ]
diff --git a/edumanage/migrations/0102_eap_versions.py b/edumanage/migrations/0102_eap_versions.py
new file mode 100644
index 00000000..fb68b52a
--- /dev/null
+++ b/edumanage/migrations/0102_eap_versions.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.29 on 2024-12-12 07:32
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('edumanage', '0101_display_ordering'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='monlocalauthnparam',
+ name='eap_method',
+ field=models.CharField(choices=[('PEAP', 'EAP-PEAP'), ('TTLS', 'EAP-TTLS'), ('TLS', 'EAP-TLS'), ('PWD', 'EAP-PWD')], max_length=16),
+ ),
+ migrations.AlterField(
+ model_name='monlocalauthnparam',
+ name='phase2',
+ field=models.CharField(choices=[('PAP', 'PAP'), ('CHAP', 'CHAP'), ('MS-CHAPv2', 'MS-CHAPv2'), ('EAP-GTC', 'GTC')], max_length=16),
+ ),
+ ]
diff --git a/edumanage/migrations/0103_nro_has_tested.py b/edumanage/migrations/0103_nro_has_tested.py
new file mode 100644
index 00000000..4d6c12e0
--- /dev/null
+++ b/edumanage/migrations/0103_nro_has_tested.py
@@ -0,0 +1,20 @@
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import edumanage.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('edumanage', '0102_eap_versions'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='serviceloc',
+ name='nro_has_tested',
+ field=models.DateField(blank=True, null=True),
+ ),
+ ]
diff --git a/edumanage/models.py b/edumanage/models.py
index fab947c9..2fa87df4 100644
--- a/edumanage/models.py
+++ b/edumanage/models.py
@@ -3,7 +3,7 @@
from collections import namedtuple
from functools import partial
import uuid
-from django.utils.inspect import getargspec
+from inspect import signature
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.contrib.contenttypes.models import ContentType
@@ -47,9 +47,9 @@ class MultiSelectFormField(forms.MultipleChoiceField):
def __init__(self, *args, **kwargs):
supercls = super(MultiSelectFormField, self)
# remove TypedChoiceField extra args
- supercls_init_args = getargspec(supercls.__init__)[0]
- if supercls_init_args[0:0] == ['self']:
- del supercls_init_args[0]
+ supercls_init_args = signature(supercls.__init__).parameters
+ if 'self' in supercls_init_args:
+ supercls_init_args.pop('self', None)
supercls.__init__(
*args,
**{key: val for (key, val) in kwargs.items()
@@ -262,6 +262,7 @@ def __contains__(self, item):
# Last member in choices tuples maps numeric to string (EDB2) ERTYPE
_ERTYPES = (
+ ('NONE', 0, 'None (Stats Only)', 'None'),
('IDP', 1, 'IdP only', 'IdP'),
('SP', 2, 'SP only', 'SP'),
('IDPSP', 3, 'IdP and SP', 'IdP+SP'),
@@ -269,7 +270,7 @@ def __contains__(self, item):
ERTYPES = get_namedtuple_choices(*_ERTYPES)
ERTYPE_ROLES = get_namedtuple_choices(*(
(cat.upper(), [val for val, descr in ERTYPES if cat in descr])
- for cat in ('IdP', 'SP')
+ for cat in ('IdP', 'SP', 'None')
))
def get_ertype_string(ertype, reverse=False):
"""
@@ -349,7 +350,7 @@ def all_names(**kwargs):
manager.all()
return ', '.join([n.name for n in names_qs])
if not lang:
- return all_names()
+ lang = getattr(settings, 'LANGUAGE_CODE', 'en')
try:
return manager.get(lang=lang).name
except Name_i18n.DoesNotExist:
@@ -385,6 +386,7 @@ def __str__(self):
class Meta:
verbose_name = "Contact"
verbose_name_plural = "Contacts"
+ ordering = ['name']
@python_2_unicode_compatible
@@ -424,6 +426,7 @@ def __str__(self):
class Meta:
verbose_name = "Instutution Contact (Pool)"
verbose_name_plural = "Instutution Contacts (Pool)"
+ ordering = ['contact__name']
@python_2_unicode_compatible
@@ -506,6 +509,7 @@ class InstRealm(models.Model):
class Meta:
verbose_name = "Institution Realm"
verbose_name_plural = "Institutions' Realms"
+ ordering = ['realm']
def __str__(self):
return '%s' % self.realm
@@ -655,12 +659,14 @@ class MonLocalAuthnParam(models.Model):
EAPTYPES = (
('PEAP', 'EAP-PEAP'),
('TTLS', 'EAP-TTLS'),
- # ('TLS', 'EAP-TLS'),
+ ('TLS', 'EAP-TLS'),
+ ('PWD', 'EAP-PWD'),
)
EAP2TYPES = (
('PAP', 'PAP'),
('CHAP', 'CHAP'),
('MS-CHAPv2', 'MS-CHAPv2'),
+ ('EAP-GTC', 'GTC'),
)
# MONRESPTYPES = (
# ('accept', 'Access-Accept expected' ),
@@ -769,6 +775,7 @@ class ServiceLoc(models.Model):
))
# only urltype = 'info' should be accepted here
url = fields.GenericRelation(URL_i18n, blank=True, null=True)
+ nro_has_tested = models.DateField(null=True, blank=True)
ts = models.DateTimeField(auto_now=True)
class Meta:
@@ -839,7 +846,7 @@ class Institution(models.Model):
)
def __str__(self):
- return "%s" % ', '.join([i.name for i in self.org_name.all()])
+ return "%s" % ', '.join([i.name for i in self.org_name.filter(lang=getattr(settings, 'LANGUAGE_CODE', 'en'))])
get_name = Name_i18n.get_name_factory('org_name')
@@ -904,12 +911,12 @@ class Meta:
def __str__(self):
return _('Institution: %(inst)s, Type: %(ertype)s') % {
# but name is many-to-many from institution
- 'inst': ', '.join([i.name for i in self.institution.org_name.all()]),
+ 'inst': ', '.join([i.name for i in self.institution.org_name.filter(lang=getattr(settings, 'LANGUAGE_CODE', 'en'))]),
'ertype': self.institution.get_ertype_display(),
}
def get_inst_name(self):
- return ", ".join([i.name for i in self.institution.org_name.all()])
+ return ", ".join([i.name for i in self.institution.org_name.filter(lang=getattr(settings, 'LANGUAGE_CODE', 'en'))])
get_inst_name.short_description = "Institution Name"
diff --git a/edumanage/views.py b/edumanage/views.py
index 7fcbbd47..5a52b024 100644
--- a/edumanage/views.py
+++ b/edumanage/views.py
@@ -35,6 +35,8 @@
from accounts.models import User
from django.core.cache import cache
from django.contrib.auth import REDIRECT_FIELD_NAME
+from django.contrib.admin.models import LogEntry, CHANGE, ADDITION, DELETION
+from django.contrib.contenttypes.models import ContentType
from edumanage.models import (
ServiceLoc,
@@ -611,7 +613,11 @@ def add_server(request, server_pk):
return HttpResponseRedirect(reverse("servers"))
if form.is_valid():
- form.save()
+ if server_pk:
+ LogEntry.objects.log_action(user_id=user.id, content_type_id=ContentType.objects.get_for_model(server).id, object_id=server_pk, object_repr=str(server), action_flag=CHANGE, change_message='Changed ' + server.name)
+ r = form.save()
+ if r and not server_pk:
+ LogEntry.objects.log_action(user_id=user.id, content_type_id=ContentType.objects.get_for_model(r).id, object_id=r.id, object_repr=str(r), action_flag=ADDITION, change_message='Added')
if not inst in form.instance.instid.all():
form.instance.instid.add(inst)
return HttpResponseRedirect(reverse("servers"))
@@ -784,6 +790,7 @@ def del_server(request):
resp['error'] = "Could not get server or you have no rights to delete"
return HttpResponse(json.dumps(resp), content_type='application/json')
try:
+ LogEntry.objects.log_action(user_id=user.id, content_type_id=ContentType.objects.get_for_model(server).id, object_id=server_pk, object_repr=str(server), action_flag=DELETION, change_message='Deleted ' + server.name)
server.delete()
except:
resp['error'] = "Could not delete server"
@@ -886,7 +893,11 @@ def add_realm(request, realm_pk):
)
return HttpResponseRedirect(reverse("realms"))
if form.is_valid():
- form.save()
+ if realm_pk:
+ LogEntry.objects.log_action(user_id=user.id, content_type_id=ContentType.objects.get_for_model(realm).id, object_id=realm_pk, object_repr=str(realm), action_flag=CHANGE, change_message='Changed ' + realm.realm)
+ r = form.save()
+ if r and not realm_pk:
+ LogEntry.objects.log_action(user_id=user.id, content_type_id=ContentType.objects.get_for_model(r).id, object_id=r.id, object_repr=str(r), action_flag=ADDITION, change_message='Added')
return HttpResponseRedirect(reverse("realms"))
else:
form.fields['instid'] = forms.ModelChoiceField(
@@ -928,6 +939,7 @@ def del_realm(request):
resp['error'] = "Could not get realm or you have no rights to delete"
return HttpResponse(json.dumps(resp), content_type='application/json')
try:
+ LogEntry.objects.log_action(user_id=user.id, content_type_id=ContentType.objects.get_for_model(realm).id, object_id=realm_pk, object_repr=str(realm), action_flag=DELETION, change_message='Deleted ' + realm.realm)
realm.delete()
except:
resp['error'] = "Could not delete realm"
@@ -1189,7 +1201,11 @@ def add_instrealmmon(request, instrealmmon_pk):
)
return HttpResponseRedirect(reverse("instrealmmon"))
if form.is_valid():
- form.save()
+ if instrealmmon_pk:
+ LogEntry.objects.log_action(user_id=user.id, content_type_id=ContentType.objects.get_for_model(instrealmmon).id, object_id=instrealmmon_pk, object_repr=str(instrealmmon), action_flag=CHANGE, change_message='Changed')
+ r = form.save()
+ if r and not instrealmmon_pk:
+ LogEntry.objects.log_action(user_id=user.id, content_type_id=ContentType.objects.get_for_model(r).id, object_id=r.id, object_repr=str(r), action_flag=ADDITION, change_message='Added')
return HttpResponseRedirect(reverse("instrealmmon"))
if instrealmmon:
edit = True
@@ -1226,6 +1242,7 @@ def del_instrealmmon(request):
pk=instrealmmon_pk,
realm__instid=institution
)
+ LogEntry.objects.log_action(user_id=user.id, content_type_id=ContentType.objects.get_for_model(instrealmmon).id, object_id=instrealmmon_pk, object_repr=str(instrealmmon), action_flag=DELETION, change_message='Deleted')
instrealmmon.delete()
except InstRealmMon.DoesNotExist:
resp['error'] = "Could not get monitored realm or you have no rights to delete"
@@ -1320,7 +1337,11 @@ def add_monlocauthpar(request, instrealmmon_pk, monlocauthpar_pk):
)
return HttpResponseRedirect(reverse("instrealmmon"))
if form.is_valid():
- form.save()
+ if monlocauthpar_pk:
+ LogEntry.objects.log_action(user_id=user.id, content_type_id=ContentType.objects.get_for_model(monlocauthpar).id, object_id=monlocauthpar_pk, object_repr=str(monlocauthpar), action_flag=CHANGE, change_message='Changed ' + monlocauthpar.username)
+ r = form.save()
+ if r and not monlocauthpar_pk:
+ LogEntry.objects.log_action(user_id=user.id, content_type_id=ContentType.objects.get_for_model(r).id, object_id=r.id, object_repr=str(r), action_flag=ADDITION, change_message='Added')
return HttpResponseRedirect(reverse("instrealmmon"))
if monlocauthpar:
edit = True
@@ -1355,6 +1376,7 @@ def del_monlocauthpar(request):
pk=monlocauthpar_pk,
instrealmmonid__realm__instid=institution
)
+ LogEntry.objects.log_action(user_id=user.id, content_type_id=ContentType.objects.get_for_model(monlocauthpar).id, object_id=monlocauthpar_pk, object_repr=str(monlocauthpar), action_flag=DELETION, change_message='Deleted ' + monlocauthpar.username)
monlocauthpar.delete()
except MonLocalAuthnParam.DoesNotExist:
resp['error'] = "Could not get realm monitoring parameters or you have no rights to delete"
@@ -1674,6 +1696,7 @@ def api(request):
def participants(request):
institutions = Institution.objects.filter(stage=PRODUCTION_STATES.ACTIVE,
institutiondetails__isnull=False).\
+ exclude(ertype__in=ERTYPE_ROLES.NONE).\
select_related('institutiondetails')
cat_instance = 'production'
dets = []
@@ -2054,6 +2077,8 @@ def cache_key(inst_key):
point['wired_no'] = u"%s" % (sl.wired_no)
point['SSID'] = u"%s" % (sl.SSID)
point['key'] = u"%s" % sl.pk
+ point['stage'] = u"%s" % (sl.stage)
+ point['nro_has_tested'] = u"%s" % (sl.nro_has_tested)
points[cache_key].append(point)
points_ret.extend(points[cache_key])
@@ -2131,7 +2156,7 @@ def instxml(request, version):
root = ElementTree.Element("institutions")
ns_xsi = "{http://www.w3.org/2001/XMLSchema-instance}"
root.set(ns_xsi + "noNamespaceSchemaLocation", "institution.xsd")
- institutions = Institution.objects.all().select_related(
+ institutions = Institution.objects.exclude(ertype__in=ERTYPE_ROLES.NONE).select_related(
'institutiondetails', 'realmid'
).prefetch_related(
'org_name', 'institutiondetails__contact',
@@ -2763,6 +2788,9 @@ def to_xml(ele, encoding="UTF-8"):
with specified *encoding*.'''
# on python3, we need to get a string, not bytestring - so if requesting unicode, request it as "unicode"
xml = ElementTree.tostring(ele, "unicode" if six.PY3 and encoding.lower()=="utf-8" else encoding)
+ # pretty print the XML to make reporting from the OT more meaningful
+ from xml.dom import minidom
+ xml = minidom.parseString(xml).toprettyxml(indent = " ")
return xml if xml.startswith('%s' % (encoding, xml)
@@ -2814,7 +2842,11 @@ def lookupShibAttr(attrmap, requestMeta):
for attr in attrmap:
if (attr in requestMeta.keys()):
if len(requestMeta[attr]) > 0:
- return requestMeta[attr]
+ # HTTP headers are encoded in latin1 (RFC2616). However, by default
+ # Shibboleth SP ignores this and puts UTF-8 encoded values into its
+ # request headers with ShibUseHeaders. So we need to fix up the
+ # resulting misencoding of accented characters
+ return bytearray(requestMeta[attr], "iso-8859-1").decode("utf-8")
return ''
# def get_i18n_name(i18n_name, lang, default_lang='en', default_name='unknown'):
diff --git a/requirements-optional.txt b/requirements-optional.txt
index 5ff9bdc9..3852da05 100644
--- a/requirements-optional.txt
+++ b/requirements-optional.txt
@@ -1,2 +1,2 @@
-Mako==1.2.2
+Mako==1.2.4
raven>=5.10.1,<=5.32.0
diff --git a/requirements.txt b/requirements.txt
index a3e2cac4..1381a55f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-Django>=1.11,<2.0
+Django==1.11.29
argparse>=1.2.1
django-registration==2.0.4
django-tinymce>=2.3.0,<3.0a
diff --git a/utils/functional.py b/utils/functional.py
index 6c41c297..d356cd5b 100644
--- a/utils/functional.py
+++ b/utils/functional.py
@@ -1,5 +1,5 @@
from functools import partial
-from django.utils.inspect import getargspec
+from inspect import signature
try:
from functools import partialmethod
except ImportError:
@@ -81,7 +81,7 @@ def __delete__(self, obj):
else:
if self.fdel is not None:
fdel_args = [obj]
- if len(getargspec(self.fdel)[0]) == 2:
+ if len(signature(self.fdel).parameters) == 2:
fdel_args.append(value)
self.fdel(*fdel_args)