Merge pull request #41 from nitmir/dev

Update version to 1.0.0
This commit is contained in:
Valentin Samir 2019-01-12 12:09:11 +01:00 committed by GitHub
commit 7e0bf93505
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 688 additions and 849 deletions

View File

@ -6,27 +6,15 @@ matrix:
- python: "2.7" - python: "2.7"
env: TOX_ENV=check_rst env: TOX_ENV=check_rst
- python: "2.7" - python: "2.7"
env: TOX_ENV=py27-django17 env: TOX_ENV=py27-django111
- python: "2.7"
env: TOX_ENV=py27-django18
- python: "2.7"
env: TOX_ENV=py27-django19
- python: "2.7"
env: TOX_ENV=py27-django110
- python: "3.4"
env: TOX_ENV=py34-django17
- python: "3.4"
env: TOX_ENV=py34-django18
- python: "3.4"
env: TOX_ENV=py34-django19
- python: "3.4"
env: TOX_ENV=py34-django110
- python: "3.5" - python: "3.5"
env: TOX_ENV=py35-django18 env: TOX_ENV=py35-django111
- python: "3.6"
env: TOX_ENV=py36-django111
- python: "3.5" - python: "3.5"
env: TOX_ENV=py35-django19 env: TOX_ENV=py35-django20
- python: "3.5" - python: "3.6"
env: TOX_ENV=py35-django110 env: TOX_ENV=py36-django20
- python: "2.7" - python: "2.7"
env: TOX_ENV=coverage env: TOX_ENV=coverage
cache: cache:

View File

@ -7,6 +7,39 @@ All notable changes to this project will be documented in this file.
:depth: 2 :depth: 2
v1.0.0 - 2019-01-12
===================
Added
-----
* Support for python 3.6 and Django 1.11
* Support for Django 2.0
* Keep query string then redirecting from / to /login
Fixes
-----
* Add missing attributes authenticationDate, longTermAuthenticationRequestTokenUsed and
isFromNewLogin from service validation response
* Catch error from calling django.contrib.staticfiles.templatetags.staticfiles.static
in non-debug mode before collectstatic in cas_server.default_settings.py
* Invalid escape sequence in regular expression
Deprecated
----------
* Support for Django <1.11 is dropped, it should still works for this version.
Next versions will most probably be not compatible with Django <1.11
* Support for python 3.4 is dropped, it should still works for this version.
Next versions may or may not works with python 3.4.
Other
-----
* Migrations have been squashed for Django 2.0 support. Be sur to apply all migration before
updating to this version
* Update PyPi url from https://pypi.python.org to https://pypi.org
v0.9.0 - 2017-11-17 v0.9.0 - 2017-11-17
=================== ===================
@ -28,7 +61,6 @@ Fixed
* Fix XSS js injection * Fix XSS js injection
v0.8.0 - 2017-03-08 v0.8.0 - 2017-03-08
=================== ===================

View File

@ -37,8 +37,8 @@ dist:
python setup.py sdist python setup.py sdist
test_venv/bin/python: test_venv/bin/python:
virtualenv test_venv python3 -m venv test_venv
test_venv/bin/pip install -U --requirement requirements-dev.txt 'Django<1.11' test_venv/bin/pip install -U --requirement requirements-dev.txt 'Django>=2.0,<2.1'
test_venv/cas/manage.py: test_venv test_venv/cas/manage.py: test_venv
mkdir -p test_venv/cas mkdir -p test_venv/cas

View File

@ -21,15 +21,15 @@ Features
* Possibility to rename/rewrite attributes per service * Possibility to rename/rewrite attributes per service
* Possibility to require some attribute values per service * Possibility to require some attribute values per service
* Federated mode between multiple CAS * Federated mode between multiple CAS
* Supports Django 1.7, 1.8 and 1.9 * Supports Django 1.11 and 2.0
* Supports Python 2.7, 3.x * Supports Python 2.7, 3.5+
Dependencies Dependencies
============ ============
``django-cas-server`` depends on the following python packages: ``django-cas-server`` depends on the following python packages:
* Django >= 1.7.1 < 1.11 * Django >= 1.11 < 2.1
* requests >= 2.4 * requests >= 2.4
* requests_futures >= 0.9.5 * requests_futures >= 0.9.5
* lxml >= 3.4 * lxml >= 3.4
@ -645,7 +645,7 @@ You could for example do as bellow::
:target: https://travis-ci.org/nitmir/django-cas-server :target: https://travis-ci.org/nitmir/django-cas-server
.. |pypi_version| image:: https://badges.genua.fr/pypi/v/django-cas-server.svg .. |pypi_version| image:: https://badges.genua.fr/pypi/v/django-cas-server.svg
:target: https://pypi.python.org/pypi/django-cas-server :target: https://pypi.org/project/django-cas-server/
.. |github_version| image:: https://badges.genua.fr/github/tag/nitmir/django-cas-server.svg?label=github .. |github_version| image:: https://badges.genua.fr/github/tag/nitmir/django-cas-server.svg?label=github
:target: https://github.com/nitmir/django-cas-server/releases/latest :target: https://github.com/nitmir/django-cas-server/releases/latest

View File

@ -11,7 +11,7 @@
"""A django CAS server application""" """A django CAS server application"""
#: version of the application #: version of the application
VERSION = '0.9.0' VERSION = '1.0.0'
#: path the the application configuration class #: path the the application configuration class
default_app_config = 'cas_server.apps.CasAppConfig' default_app_config = 'cas_server.apps.CasAppConfig'

View File

@ -206,7 +206,7 @@ class CASClientV2(CASClientBase, ReturnUnicode):
def parse_attributes_xml_element(cls, element, charset): def parse_attributes_xml_element(cls, element, charset):
attributes = dict() attributes = dict()
for attribute in element: for attribute in element:
tag = cls.self.u(attribute.tag, charset).split(u"}").pop() tag = cls.u(attribute.tag, charset).split(u"}").pop()
if tag in attributes: if tag in attributes:
if isinstance(attributes[tag], list): if isinstance(attributes[tag], list):
attributes[tag].append(cls.u(attribute.text, charset)) attributes[tag].append(cls.u(attribute.text, charset))

View File

@ -17,10 +17,20 @@ from django.utils.translation import ugettext_lazy as _
from importlib import import_module from importlib import import_module
#: URL to the logo showed in the up left corner on the default templates. try:
CAS_LOGO_URL = static("cas_server/logo.png") #: URL to the logo showed in the up left corner on the default templates.
#: URL to the favicon (shortcut icon) used by the default templates. Default is a key icon. CAS_LOGO_URL = static("cas_server/logo.png")
CAS_FAVICON_URL = static("cas_server/favicon.ico") #: URL to the favicon (shortcut icon) used by the default templates. Default is a key icon.
CAS_FAVICON_URL = static("cas_server/favicon.ico")
# is settings.DEBUG is False and collectstatics has not been run yet, the static function will
# raise a ValueError because the file is not found.
except ValueError:
#: URL to the logo showed in the up left corner on the default templates.
CAS_LOGO_URL = None
#: URL to the favicon (shortcut icon) used by the default templates. Default is a key icon.
CAS_FAVICON_URL = None
#: Show the powered by footer if set to ``True`` #: Show the powered by footer if set to ``True``
CAS_SHOW_POWERED = True CAS_SHOW_POWERED = True
#: URLs to css and javascript external components. #: URLs to css and javascript external components.
@ -183,7 +193,7 @@ CAS_NEW_VERSION_HTML_WARNING = True
CAS_NEW_VERSION_EMAIL_WARNING = True CAS_NEW_VERSION_EMAIL_WARNING = True
#: URL to the pypi json of the application. Used to retreive the version number of the last version. #: URL to the pypi json of the application. Used to retreive the version number of the last version.
#: You should not change it. #: You should not change it.
CAS_NEW_VERSION_JSON_URL = "https://pypi.python.org/pypi/django-cas-server/json" CAS_NEW_VERSION_JSON_URL = "https://pypi.org/pypi/django-cas-server/json"
#: If the service message should be displayed on the login page #: If the service message should be displayed on the login page
CAS_SHOW_SERVICE_MESSAGES = True CAS_SHOW_SERVICE_MESSAGES = True

View File

@ -0,0 +1,469 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.8 on 2018-04-29 17:40
from __future__ import unicode_literals
import cas_server.utils
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
replaces = [('cas_server', '0001_squashed_0021_auto_20150611_2102'), ('cas_server', '0002_auto_20151212_1300'), ('cas_server', '0003_auto_20151212_1721'), ('cas_server', '0004_auto_20151218_1032'), ('cas_server', '0005_auto_20160616_1018'), ('cas_server', '0006_auto_20160706_1727'), ('cas_server', '0007_auto_20160723_2252'), ('cas_server', '0008_newversionwarning'), ('cas_server', '0009_auto_20160814_0619'), ('cas_server', '0010_auto_20160824_2112'), ('cas_server', '0011_auto_20161007_1258'), ('cas_server', '0012_auto_20170328_1610'), ('cas_server', '0013_auto_20170329_1748')]
initial = True
dependencies = [
('sessions', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Proxy',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('url', models.CharField(max_length=255)),
],
options={
'ordering': ('-pk',),
},
),
migrations.CreateModel(
name='ProxyGrantingTicket',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('attributs', models.TextField(blank=True, default=None, null=True)),
('validate', models.BooleanField(default=False)),
('service', models.TextField()),
('creation', models.DateTimeField(auto_now_add=True)),
('renew', models.BooleanField(default=False)),
('value', models.CharField(default=cas_server.utils.gen_pgt, max_length=255, unique=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='ProxyTicket',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('attributs', models.TextField(blank=True, default=None, null=True)),
('validate', models.BooleanField(default=False)),
('service', models.TextField()),
('creation', models.DateTimeField(auto_now_add=True)),
('renew', models.BooleanField(default=False)),
('value', models.CharField(default=cas_server.utils.gen_pt, max_length=255, unique=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='ServicePattern',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('pos', models.IntegerField(default=100)),
('pattern', models.CharField(max_length=255, unique=True)),
('user_field', models.CharField(blank=True, default=b'', help_text=b"Nom de l'attribut transmit comme username, vide = login", max_length=255)),
('usernames', models.CharField(blank=True, default=b'', help_text=b"Liste d'utilisateurs accept\xc3\xa9s s\xc3\xa9par\xc3\xa9 par des virgules, vide = tous les utilisateur", max_length=255)),
('attributs', models.CharField(blank=True, default=b'', help_text=b"Liste des nom d'attributs \xc3\xa0 transmettre au service, s\xc3\xa9par\xc3\xa9 par une virgule. vide = aucun", max_length=255)),
('proxy', models.BooleanField(default=False, help_text=b"Un ProxyGrantingTicket peut \xc3\xaatre d\xc3\xa9livr\xc3\xa9 au service pour s'authentifier en temps que l'utilisateur sur d'autres services")),
('filter', models.CharField(blank=True, default=b'', help_text=b'Une lambda fonction pour filtrer sur les utilisateur o\xc3\xb9 leurs attribut, arg1: username, arg2:attrs_dict. vide = pas de filtre', max_length=255)),
],
options={
'ordering': ('pos',),
},
),
migrations.CreateModel(
name='ServiceTicket',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('attributs', models.TextField(blank=True, default=None, null=True)),
('validate', models.BooleanField(default=False)),
('service', models.TextField()),
('creation', models.DateTimeField(auto_now_add=True)),
('renew', models.BooleanField(default=False)),
('value', models.CharField(default=cas_server.utils.gen_st, max_length=255, unique=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(max_length=30)),
('date', models.DateTimeField(auto_now=True)),
('session_key', models.CharField(blank=True, max_length=40, null=True)),
],
),
migrations.AddField(
model_name='serviceticket',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='serviceticket', to='cas_server.User'),
),
migrations.AddField(
model_name='proxyticket',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='proxyticket', to='cas_server.User'),
),
migrations.AddField(
model_name='proxygrantingticket',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='proxygrantingticket', to='cas_server.User'),
),
migrations.AddField(
model_name='proxy',
name='proxy_ticket',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='proxies', to='cas_server.ProxyTicket'),
),
migrations.AddField(
model_name='proxygrantingticket',
name='service_pattern',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='proxygrantingticket', to='cas_server.ServicePattern'),
preserve_default=False,
),
migrations.AddField(
model_name='proxyticket',
name='service_pattern',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='proxyticket', to='cas_server.ServicePattern'),
preserve_default=False,
),
migrations.AddField(
model_name='serviceticket',
name='service_pattern',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='serviceticket', to='cas_server.ServicePattern'),
preserve_default=False,
),
migrations.CreateModel(
name='ReplaceAttributName',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text="nom d'un attributs \xe0 transmettre au service", max_length=255)),
('replace', models.CharField(blank=True, help_text="nom sous lequel l'attribut sera pr\xe9sent\xe9 au service. vide = inchang\xe9", max_length=255)),
('service_pattern', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attributs', to='cas_server.ServicePattern')),
],
),
migrations.RemoveField(
model_name='servicepattern',
name='attributs',
),
migrations.CreateModel(
name='FilterAttributValue',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('attribut', models.CharField(help_text='Name of the attribute which must verify pattern', max_length=255, verbose_name='attribute')),
('pattern', models.CharField(help_text='a regular expression', max_length=255, validators=[cas_server.utils.regexpr_validator], verbose_name='pattern')),
('service_pattern', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='filters', to='cas_server.ServicePattern')),
],
),
migrations.CreateModel(
name='ReplaceAttributValue',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('attribut', models.CharField(help_text='Name of the attribute for which the value must be replace', max_length=255, verbose_name='attribute')),
('pattern', models.CharField(help_text='An regular expression maching whats need to be replaced', max_length=255, validators=[cas_server.utils.regexpr_validator], verbose_name='pattern')),
('replace', models.CharField(blank=True, help_text='replace expression, groups are capture by \\1, \\2 \u2026', max_length=255, verbose_name='replace')),
('service_pattern', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='replacements', to='cas_server.ServicePattern')),
],
),
migrations.RemoveField(
model_name='servicepattern',
name='filter',
),
migrations.CreateModel(
name='Username',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('value', models.CharField(help_text='username allowed to connect to the service', max_length=255, verbose_name='username')),
('service_pattern', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='usernames', to='cas_server.ServicePattern')),
],
),
migrations.RemoveField(
model_name='servicepattern',
name='usernames',
),
migrations.AddField(
model_name='servicepattern',
name='restrict_users',
field=models.BooleanField(default=False, help_text='Limit username allowed to connect to the list provided bellow', verbose_name='restrict username'),
),
migrations.AddField(
model_name='servicepattern',
name='name',
field=models.CharField(blank=True, help_text='A name for the service', max_length=255, null=True, unique=True, verbose_name='name'),
),
migrations.AlterUniqueTogether(
name='replaceattributname',
unique_together=set([('name', 'replace', 'service_pattern')]),
),
migrations.AddField(
model_name='servicepattern',
name='single_log_out',
field=models.BooleanField(default=False, help_text='Enable SLO for the service', verbose_name='single log out'),
),
migrations.AlterField(
model_name='replaceattributname',
name='name',
field=models.CharField(help_text='name of an attribut to send to the service', max_length=255, verbose_name='name'),
),
migrations.AlterField(
model_name='replaceattributname',
name='replace',
field=models.CharField(blank=True, help_text='name under which the attribut will be showto the service. empty = default name of the attribut', max_length=255, verbose_name='replace'),
),
migrations.AlterField(
model_name='servicepattern',
name='pattern',
field=models.CharField(max_length=255, unique=True, verbose_name='pattern'),
),
migrations.AlterField(
model_name='servicepattern',
name='pos',
field=models.IntegerField(default=100, verbose_name='position'),
),
migrations.AlterField(
model_name='servicepattern',
name='proxy',
field=models.BooleanField(default=False, help_text='A ProxyGrantingTicket can be delivered to the service in order to authenticate for the user on a backend service', verbose_name='proxy'),
),
migrations.AlterField(
model_name='servicepattern',
name='user_field',
field=models.CharField(blank=True, default=b'', help_text='Name of the attribut to transmit as username, empty = login', max_length=255, verbose_name='user field'),
),
migrations.AddField(
model_name='proxygrantingticket',
name='single_log_out',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='proxyticket',
name='single_log_out',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='serviceticket',
name='single_log_out',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='servicepattern',
name='proxy_callback',
field=models.BooleanField(default=False, help_text='can be used as a proxy callback to deliver PGT', verbose_name='proxy callback'),
),
migrations.AlterField(
model_name='servicepattern',
name='proxy',
field=models.BooleanField(default=False, help_text='Proxy tickets can be delivered to the service', verbose_name='proxy'),
),
migrations.AddField(
model_name='servicepattern',
name='single_log_out_callback',
field=models.CharField(blank=True, default=b'', help_text='URL where the SLO request will be POST. empty = service url\nThis is usefull for non HTTP proxied services.', max_length=255, verbose_name='single log out callback'),
),
migrations.AlterField(
model_name='replaceattributname',
name='name',
field=models.CharField(help_text='name of an attribut to send to the service, use * for all attributes', max_length=255, verbose_name='name'),
),
migrations.AlterUniqueTogether(
name='user',
unique_together=set([('username', 'session_key')]),
),
migrations.AlterField(
model_name='servicepattern',
name='pattern',
field=models.CharField(help_text="A regular expression matching services. Will usually looks like '^https://some\\.server\\.com/path/.*$'.As it is a regular expression, special character must be escaped with a '\\'.", max_length=255, unique=True, verbose_name='pattern'),
),
migrations.AlterModelOptions(
name='servicepattern',
options={'ordering': ('pos',), 'verbose_name': 'Service pattern', 'verbose_name_plural': 'Services patterns'},
),
migrations.AlterModelOptions(
name='user',
options={'verbose_name': 'User', 'verbose_name_plural': 'Users'},
),
migrations.AlterField(
model_name='servicepattern',
name='pos',
field=models.IntegerField(default=100, help_text='service patterns are sorted using the position attribute', verbose_name='position'),
),
migrations.CreateModel(
name='FederatedIendityProvider',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('suffix', models.CharField(help_text='Suffix append to backend CAS returner username: `returned_username`@`suffix`', max_length=30, unique=True, verbose_name='suffix')),
('server_url', models.CharField(max_length=255, verbose_name='server url')),
('cas_protocol_version', models.CharField(choices=[(b'1', b'CAS 1.0'), (b'2', b'CAS 2.0'), (b'3', b'CAS 3.0'), (b'CAS_2_SAML_1_0', b'SAML 1.1')], default=b'3', help_text='Version of the CAS protocol to use when sending requests the the backend CAS', max_length=30, verbose_name='CAS protocol version')),
('verbose_name', models.CharField(help_text='Name for this identity provider displayed on the login page', max_length=255, verbose_name='verbose name')),
('pos', models.IntegerField(default=100, help_text='Identity provider are sorted using the (position, verbose name, suffix) attributes', verbose_name='position')),
('display', models.BooleanField(default=True, help_text='Display the provider on the login page', verbose_name='display')),
],
options={
'verbose_name': 'identity provider',
'verbose_name_plural': 'identity providers',
},
),
migrations.CreateModel(
name='FederatedUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(max_length=124)),
('provider', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cas_server.FederatedIendityProvider')),
('ticket', models.CharField(max_length=255)),
('last_update', models.DateTimeField(auto_now=True)),
('_attributs', models.TextField(blank=True, default=None, null=True)),
],
),
migrations.AlterUniqueTogether(
name='federateduser',
unique_together=set([('username', 'provider')]),
),
migrations.CreateModel(
name='FederateSLO',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(max_length=30)),
('session_key', models.CharField(blank=True, max_length=40, null=True)),
('ticket', models.CharField(db_index=True, max_length=255)),
],
),
migrations.AlterUniqueTogether(
name='federateslo',
unique_together=set([('username', 'session_key', 'ticket')]),
),
migrations.AlterField(
model_name='federatediendityprovider',
name='cas_protocol_version',
field=models.CharField(choices=[(b'1', b'CAS 1.0'), (b'2', b'CAS 2.0'), (b'3', b'CAS 3.0'), (b'CAS_2_SAML_1_0', b'SAML 1.1')], default=b'3', help_text='Version of the CAS protocol to use when sending requests the the backend CAS.', max_length=30, verbose_name='CAS protocol version'),
),
migrations.AlterField(
model_name='federatediendityprovider',
name='display',
field=models.BooleanField(default=True, help_text='Display the provider on the login page.', verbose_name='display'),
),
migrations.AlterField(
model_name='federatediendityprovider',
name='pos',
field=models.IntegerField(default=100, help_text='Position of the identity provider on the login page. Identity provider are sorted using the (position, verbose name, suffix) attributes.', verbose_name='position'),
),
migrations.AlterField(
model_name='federatediendityprovider',
name='suffix',
field=models.CharField(help_text='Suffix append to backend CAS returner username: ``returned_username`` @ ``suffix``.', max_length=30, unique=True, verbose_name='suffix'),
),
migrations.AlterField(
model_name='federatediendityprovider',
name='verbose_name',
field=models.CharField(help_text='Name for this identity provider displayed on the login page.', max_length=255, verbose_name='verbose name'),
),
migrations.RemoveField(
model_name='proxygrantingticket',
name='attributs',
),
migrations.RemoveField(
model_name='proxyticket',
name='attributs',
),
migrations.RemoveField(
model_name='serviceticket',
name='attributs',
),
migrations.AddField(
model_name='proxygrantingticket',
name='_attributs',
field=models.TextField(blank=True, default=None, null=True),
),
migrations.AddField(
model_name='proxyticket',
name='_attributs',
field=models.TextField(blank=True, default=None, null=True),
),
migrations.AddField(
model_name='serviceticket',
name='_attributs',
field=models.TextField(blank=True, default=None, null=True),
),
migrations.AlterField(
model_name='federatediendityprovider',
name='suffix',
field=models.CharField(help_text='Suffix append to backend CAS returned username: ``returned_username`` @ ``suffix``.', max_length=30, unique=True, verbose_name='suffix'),
),
migrations.CreateModel(
name='NewVersionWarning',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('version', models.CharField(max_length=255)),
],
),
migrations.AlterField(
model_name='replaceattributname',
name='name',
field=models.CharField(help_text='name of an attribute to send to the service, use * for all attributes', max_length=255, verbose_name='name'),
),
migrations.AlterField(
model_name='replaceattributname',
name='replace',
field=models.CharField(blank=True, help_text='name under which the attribute will be showto the service. empty = default name of the attribut', max_length=255, verbose_name='replace'),
),
migrations.AlterField(
model_name='servicepattern',
name='user_field',
field=models.CharField(blank=True, default=b'', help_text='Name of the attribute to transmit as username, empty = login', max_length=255, verbose_name='user field'),
),
migrations.AlterField(
model_name='replaceattributname',
name='replace',
field=models.CharField(blank=True, help_text='name under which the attribute will be show to the service. empty = default name of the attribut', max_length=255, verbose_name='replace'),
),
migrations.AlterField(
model_name='servicepattern',
name='pattern',
field=models.CharField(help_text="A regular expression matching services. Will usually looks like '^https://some\\.server\\.com/path/.*$'.As it is a regular expression, special character must be escaped with a '\\'.", max_length=255, unique=True, validators=[cas_server.utils.regexpr_validator], verbose_name='pattern'),
),
migrations.CreateModel(
name='UserAttributes',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('_attributs', models.TextField(blank=True, default=None, null=True)),
('username', models.CharField(max_length=155, unique=True)),
],
options={
'verbose_name': 'User attributes cache',
'verbose_name_plural': 'User attributes caches',
},
),
migrations.AlterModelOptions(
name='federateduser',
options={'verbose_name': 'Federated user', 'verbose_name_plural': 'Federated users'},
),
migrations.AddField(
model_name='user',
name='last_login',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AlterField(
model_name='federatediendityprovider',
name='cas_protocol_version',
field=models.CharField(choices=[('1', 'CAS 1.0'), ('2', 'CAS 2.0'), ('3', 'CAS 3.0'), ('CAS_2_SAML_1_0', 'SAML 1.1')], default='3', help_text='Version of the CAS protocol to use when sending requests the the backend CAS.', max_length=30, verbose_name='CAS protocol version'),
),
migrations.AlterField(
model_name='servicepattern',
name='single_log_out_callback',
field=models.CharField(blank=True, default='', help_text='URL where the SLO request will be POST. empty = service url\nThis is usefull for non HTTP proxied services.', max_length=255, verbose_name='single log out callback'),
),
migrations.AlterField(
model_name='servicepattern',
name='user_field',
field=models.CharField(blank=True, default='', help_text='Name of the attribute to transmit as username, empty = login', max_length=255, verbose_name='user field'),
),
migrations.AlterField(
model_name='user',
name='username',
field=models.CharField(max_length=250),
),
]

View File

@ -1,316 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import django.db.models.deletion
import cas_server.utils
class Migration(migrations.Migration):
#replaces = [(b'cas_server', '0001_initial'), (b'cas_server', '0002_auto_20150517_1406'), (b'cas_server', '0003_auto_20150518_1648'), (b'cas_server', '0004_auto_20150518_1659'), (b'cas_server', '0005_auto_20150518_1717'), (b'cas_server', '0006_auto_20150518_1720'), (b'cas_server', '0007_auto_20150518_1727'), (b'cas_server', '0008_servicepattern_name'), (b'cas_server', '0009_auto_20150518_1740'), (b'cas_server', '0010_auto_20150518_2139'), (b'cas_server', '0011_auto_20150523_1731'), (b'cas_server', '0012_auto_20150527_1956'), (b'cas_server', '0013_servicepattern_single_sign_out'), (b'cas_server', '0014_auto_20150528_0012'), (b'cas_server', '0015_auto_20150528_1202'), (b'cas_server', '0016_auto_20150528_1326'), (b'cas_server', '0017_remove_user_attributs'), (b'cas_server', '0018_auto_20150608_1621'), (b'cas_server', '0019_auto_20150609_1903'), (b'cas_server', '0020_auto_20150609_1917'), (b'cas_server', '0021_auto_20150611_2102')]
dependencies = [
('sessions', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Proxy',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('url', models.CharField(max_length=255)),
],
options={
'ordering': ('-pk',),
},
bases=(models.Model,),
),
migrations.CreateModel(
name='ProxyGrantingTicket',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('attributs', models.TextField(blank=True, default=None, null=True)),
('validate', models.BooleanField(default=False)),
('service', models.TextField()),
('creation', models.DateTimeField(auto_now_add=True)),
('renew', models.BooleanField(default=False)),
('value', models.CharField(default=cas_server.utils.gen_pgt, unique=True, max_length=255)),
],
options={
'abstract': False,
},
bases=(models.Model,),
),
migrations.CreateModel(
name='ProxyTicket',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('attributs', models.TextField(blank=True, default=None, null=True)),
('validate', models.BooleanField(default=False)),
('service', models.TextField()),
('creation', models.DateTimeField(auto_now_add=True)),
('renew', models.BooleanField(default=False)),
('value', models.CharField(default=cas_server.utils.gen_pt, unique=True, max_length=255)),
],
options={
'abstract': False,
},
bases=(models.Model,),
),
migrations.CreateModel(
name='ServicePattern',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('pos', models.IntegerField(default=100)),
('pattern', models.CharField(unique=True, max_length=255)),
('user_field', models.CharField(default=b'', help_text=b"Nom de l'attribut transmit comme username, vide = login", max_length=255, blank=True)),
('usernames', models.CharField(default=b'', help_text=b"Liste d'utilisateurs accept\xc3\xa9s s\xc3\xa9par\xc3\xa9 par des virgules, vide = tous les utilisateur", max_length=255, blank=True)),
('attributs', models.CharField(default=b'', help_text=b"Liste des nom d'attributs \xc3\xa0 transmettre au service, s\xc3\xa9par\xc3\xa9 par une virgule. vide = aucun", max_length=255, blank=True)),
('proxy', models.BooleanField(default=False, help_text=b"Un ProxyGrantingTicket peut \xc3\xaatre d\xc3\xa9livr\xc3\xa9 au service pour s'authentifier en temps que l'utilisateur sur d'autres services")),
('filter', models.CharField(default=b'', help_text=b'Une lambda fonction pour filtrer sur les utilisateur o\xc3\xb9 leurs attribut, arg1: username, arg2:attrs_dict. vide = pas de filtre', max_length=255, blank=True)),
],
options={
'ordering': ('pos',),
},
bases=(models.Model,),
),
migrations.CreateModel(
name='ServiceTicket',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('attributs', models.TextField(blank=True, default=None, null=True)),
('validate', models.BooleanField(default=False)),
('service', models.TextField()),
('creation', models.DateTimeField(auto_now_add=True)),
('renew', models.BooleanField(default=False)),
('value', models.CharField(default=cas_server.utils.gen_st, unique=True, max_length=255)),
],
options={
'abstract': False,
},
bases=(models.Model,),
),
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('username', models.CharField(max_length=30)),
('date', models.DateTimeField(auto_now=True, auto_now_add=True)),
('session_key', models.CharField(max_length=40, null=True, blank=True)),
],
options={
},
bases=(models.Model,),
),
migrations.AddField(
model_name='serviceticket',
name='user',
field=models.ForeignKey(related_name='serviceticket', to='cas_server.User'),
preserve_default=True,
),
migrations.AddField(
model_name='proxyticket',
name='user',
field=models.ForeignKey(related_name='proxyticket', to='cas_server.User'),
preserve_default=True,
),
migrations.AddField(
model_name='proxygrantingticket',
name='user',
field=models.ForeignKey(related_name='proxygrantingticket', to='cas_server.User'),
preserve_default=True,
),
migrations.AddField(
model_name='proxy',
name='proxy_ticket',
field=models.ForeignKey(related_name='proxies', to='cas_server.ProxyTicket'),
preserve_default=True,
),
migrations.AddField(
model_name='proxygrantingticket',
name='service_pattern',
field=models.ForeignKey(related_name='proxygrantingticket', default=1, to='cas_server.ServicePattern'),
preserve_default=False,
),
migrations.AddField(
model_name='proxyticket',
name='service_pattern',
field=models.ForeignKey(related_name='proxyticket', default=1, to='cas_server.ServicePattern'),
preserve_default=False,
),
migrations.AddField(
model_name='serviceticket',
name='service_pattern',
field=models.ForeignKey(related_name='serviceticket', default=1, to='cas_server.ServicePattern'),
preserve_default=False,
),
migrations.CreateModel(
name='ReplaceAttributName',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(help_text="nom d'un attributs \xe0 transmettre au service", max_length=255)),
('replace', models.CharField(help_text="nom sous lequel l'attribut sera pr\xe9sent\xe9 au service. vide = inchang\xe9", max_length=255, blank=True)),
('service_pattern', models.ForeignKey(related_name='attributs', to='cas_server.ServicePattern')),
],
options={
},
bases=(models.Model,),
),
migrations.RemoveField(
model_name='servicepattern',
name='attributs',
),
migrations.CreateModel(
name='FilterAttributValue',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('attribut', models.CharField(help_text='Name of the attribut which must verify pattern', max_length=255, verbose_name='attribut')),
('pattern', models.CharField(help_text='a regular expression', max_length=255, verbose_name='pattern')),
('service_pattern', models.ForeignKey(related_name='filters', to='cas_server.ServicePattern')),
],
options={
},
bases=(models.Model,),
),
migrations.CreateModel(
name='ReplaceAttributValue',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('attribut', models.CharField(help_text='Name of the attribut for which the value must be replace', max_length=255, verbose_name='attribut')),
('pattern', models.CharField(help_text='An regular expression maching whats need to be replaced', max_length=255, verbose_name='pattern')),
('replace', models.CharField(help_text='replace expression, groups are capture by \\1, \\2 \u2026', max_length=255, verbose_name='replace', blank=True)),
('service_pattern', models.ForeignKey(related_name='replacements', to='cas_server.ServicePattern')),
],
options={
},
bases=(models.Model,),
),
migrations.RemoveField(
model_name='servicepattern',
name='filter',
),
migrations.CreateModel(
name='Username',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('value', models.CharField(help_text='username allowed to connect to the service', max_length=255, verbose_name='username')),
('service_pattern', models.ForeignKey(related_name='usernames', to='cas_server.ServicePattern')),
],
options={
},
bases=(models.Model,),
),
migrations.RemoveField(
model_name='servicepattern',
name='usernames',
),
migrations.AddField(
model_name='servicepattern',
name='restrict_users',
field=models.BooleanField(default=False, help_text='Limit username allowed to connect to the list provided bellow', verbose_name='restrict username'),
preserve_default=True,
),
migrations.AddField(
model_name='servicepattern',
name='name',
field=models.CharField(null=True, max_length=255, blank=True, help_text='A name for the service', unique=True, verbose_name='name'),
preserve_default=True,
),
migrations.AlterUniqueTogether(
name='replaceattributname',
unique_together=set([('name', 'service_pattern')]),
),
migrations.AlterUniqueTogether(
name='replaceattributname',
unique_together=set([('name', 'replace', 'service_pattern')]),
),
migrations.AddField(
model_name='servicepattern',
name='single_log_out',
field=models.BooleanField(default=False, help_text='Enable SLO for the service', verbose_name='single log out'),
preserve_default=True,
),
migrations.AlterField(
model_name='replaceattributname',
name='name',
field=models.CharField(help_text='name of an attribut to send to the service', max_length=255, verbose_name='name'),
preserve_default=True,
),
migrations.AlterField(
model_name='replaceattributname',
name='replace',
field=models.CharField(help_text='name under which the attribut will be showto the service. empty = default name of the attribut', max_length=255, verbose_name='replace', blank=True),
preserve_default=True,
),
migrations.AlterField(
model_name='servicepattern',
name='pattern',
field=models.CharField(unique=True, max_length=255, verbose_name='pattern'),
preserve_default=True,
),
migrations.AlterField(
model_name='servicepattern',
name='pos',
field=models.IntegerField(default=100, verbose_name='position'),
preserve_default=True,
),
migrations.AlterField(
model_name='servicepattern',
name='proxy',
field=models.BooleanField(default=False, help_text='A ProxyGrantingTicket can be delivered to the service in order to authenticate for the user on a backend service', verbose_name='proxy'),
preserve_default=True,
),
migrations.AlterField(
model_name='servicepattern',
name='user_field',
field=models.CharField(default=b'', help_text='Name of the attribut to transmit as username, empty = login', max_length=255, verbose_name='user field', blank=True),
preserve_default=True,
),
migrations.AddField(
model_name='proxygrantingticket',
name='single_log_out',
field=models.BooleanField(default=False),
preserve_default=True,
),
migrations.AddField(
model_name='proxyticket',
name='single_log_out',
field=models.BooleanField(default=False),
preserve_default=True,
),
migrations.AddField(
model_name='serviceticket',
name='single_log_out',
field=models.BooleanField(default=False),
preserve_default=True,
),
migrations.AddField(
model_name='servicepattern',
name='proxy_callback',
field=models.BooleanField(default=False, help_text='can be used as a proxy callback to deliver PGT', verbose_name='proxy callback'),
preserve_default=True,
),
migrations.AlterField(
model_name='servicepattern',
name='proxy',
field=models.BooleanField(default=False, help_text='Proxy tickets can be delivered to the service', verbose_name='proxy'),
preserve_default=True,
),
migrations.AddField(
model_name='servicepattern',
name='single_log_out_callback',
field=models.CharField(default=b'', help_text='URL where the SLO request will be POST. empty = service url\nThis is usefull for non HTTP proxied services.', max_length=255, verbose_name='single log out callback', blank=True),
preserve_default=True,
),
migrations.AlterField(
model_name='replaceattributname',
name='name',
field=models.CharField(help_text='name of an attribut to send to the service, use * for all attributes', max_length=255, verbose_name='name'),
preserve_default=True,
),
migrations.AlterUniqueTogether(
name='user',
unique_together=set([('username', 'session_key')]),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('cas_server', '0001_squashed_0021_auto_20150611_2102'),
]
operations = [
migrations.AlterField(
model_name='user',
name='date',
field=models.DateTimeField(auto_now=True),
preserve_default=True,
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('cas_server', '0002_auto_20151212_1300'),
]
operations = [
migrations.AlterField(
model_name='servicepattern',
name='pattern',
field=models.CharField(help_text="A regular expression matching services. Will usually looks like '^https://some\\.server\\.com/path/.*$'.As it is a regular expression, special character must be escaped with a '\\'.", unique=True, max_length=255, verbose_name='pattern'),
preserve_default=True,
),
]

View File

@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('cas_server', '0003_auto_20151212_1721'),
]
operations = [
migrations.AlterModelOptions(
name='servicepattern',
options={'ordering': ('pos',), 'verbose_name': 'Service pattern', 'verbose_name_plural': 'Services patterns'},
),
migrations.AlterModelOptions(
name='user',
options={'verbose_name': 'User', 'verbose_name_plural': 'Users'},
),
]

View File

@ -1,65 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.6 on 2016-06-16 10:18
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('cas_server', '0004_auto_20151218_1032'),
]
operations = [
migrations.AlterField(
model_name='servicepattern',
name='pos',
field=models.IntegerField(default=100, help_text='service patterns are sorted using the position attribute', verbose_name='position'),
),
migrations.CreateModel(
name='FederatedIendityProvider',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('suffix', models.CharField(help_text='Suffix append to backend CAS returner username: `returned_username`@`suffix`', max_length=30, unique=True, verbose_name='suffix')),
('server_url', models.CharField(max_length=255, verbose_name='server url')),
('cas_protocol_version', models.CharField(choices=[(b'1', b'CAS 1.0'), (b'2', b'CAS 2.0'), (b'3', b'CAS 3.0'), (b'CAS_2_SAML_1_0', b'SAML 1.1')], default=b'3', help_text='Version of the CAS protocol to use when sending requests the the backend CAS', max_length=30, verbose_name='CAS protocol version')),
('verbose_name', models.CharField(help_text='Name for this identity provider displayed on the login page', max_length=255, verbose_name='verbose name')),
('pos', models.IntegerField(default=100, help_text='Identity provider are sorted using the (position, verbose name, suffix) attributes', verbose_name='position')),
('display', models.BooleanField(default=True, help_text='Display the provider on the login page', verbose_name='display')),
],
options={
'verbose_name': 'identity provider',
'verbose_name_plural': 'identity providers',
},
),
migrations.CreateModel(
name='FederatedUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(max_length=124)),
('provider', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cas_server.FederatedIendityProvider')),
('attributs', models.TextField(blank=True, default=None, null=True)),
('ticket', models.CharField(max_length=255)),
('last_update', models.DateTimeField(auto_now=True)),
],
),
migrations.AlterUniqueTogether(
name='federateduser',
unique_together=set([('username', 'provider')]),
),
migrations.CreateModel(
name='FederateSLO',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(max_length=30)),
('session_key', models.CharField(blank=True, max_length=40, null=True)),
('ticket', models.CharField(db_index=True, max_length=255)),
],
),
migrations.AlterUniqueTogether(
name='federateslo',
unique_together=set([('username', 'session_key', 'ticket')]),
),
]

View File

@ -1,40 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-07-06 17:27
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cas_server', '0005_auto_20160616_1018'),
]
operations = [
migrations.AlterField(
model_name='federatediendityprovider',
name='cas_protocol_version',
field=models.CharField(choices=[(b'1', b'CAS 1.0'), (b'2', b'CAS 2.0'), (b'3', b'CAS 3.0'), (b'CAS_2_SAML_1_0', b'SAML 1.1')], default=b'3', help_text='Version of the CAS protocol to use when sending requests the the backend CAS.', max_length=30, verbose_name='CAS protocol version'),
),
migrations.AlterField(
model_name='federatediendityprovider',
name='display',
field=models.BooleanField(default=True, help_text='Display the provider on the login page.', verbose_name='display'),
),
migrations.AlterField(
model_name='federatediendityprovider',
name='pos',
field=models.IntegerField(default=100, help_text='Position of the identity provider on the login page. Identity provider are sorted using the (position, verbose name, suffix) attributes.', verbose_name='position'),
),
migrations.AlterField(
model_name='federatediendityprovider',
name='suffix',
field=models.CharField(help_text='Suffix append to backend CAS returner username: ``returned_username`` @ ``suffix``.', max_length=30, unique=True, verbose_name='suffix'),
),
migrations.AlterField(
model_name='federatediendityprovider',
name='verbose_name',
field=models.CharField(help_text='Name for this identity provider displayed on the login page.', max_length=255, verbose_name='verbose name'),
),
]

View File

@ -1,56 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.8 on 2016-07-23 22:52
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cas_server', '0006_auto_20160706_1727'),
]
operations = [
migrations.RemoveField(
model_name='federateduser',
name='attributs',
),
migrations.RemoveField(
model_name='proxygrantingticket',
name='attributs',
),
migrations.RemoveField(
model_name='proxyticket',
name='attributs',
),
migrations.RemoveField(
model_name='serviceticket',
name='attributs',
),
migrations.AddField(
model_name='federateduser',
name='_attributs',
field=models.TextField(blank=True, default=None, null=True),
),
migrations.AddField(
model_name='proxygrantingticket',
name='_attributs',
field=models.TextField(blank=True, default=None, null=True),
),
migrations.AddField(
model_name='proxyticket',
name='_attributs',
field=models.TextField(blank=True, default=None, null=True),
),
migrations.AddField(
model_name='serviceticket',
name='_attributs',
field=models.TextField(blank=True, default=None, null=True),
),
migrations.AlterField(
model_name='federatediendityprovider',
name='suffix',
field=models.CharField(help_text='Suffix append to backend CAS returned username: ``returned_username`` @ ``suffix``.', max_length=30, unique=True, verbose_name='suffix'),
),
]

View File

@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-07-27 21:59
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cas_server', '0007_auto_20160723_2252'),
]
operations = [
migrations.CreateModel(
name='NewVersionWarning',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('version', models.CharField(max_length=255)),
],
),
]

View File

@ -1,40 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.8 on 2016-08-14 06:19
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cas_server', '0008_newversionwarning'),
]
operations = [
migrations.AlterField(
model_name='filterattributvalue',
name='attribut',
field=models.CharField(help_text='Name of the attribute which must verify pattern', max_length=255, verbose_name='attribute'),
),
migrations.AlterField(
model_name='replaceattributname',
name='name',
field=models.CharField(help_text='name of an attribute to send to the service, use * for all attributes', max_length=255, verbose_name='name'),
),
migrations.AlterField(
model_name='replaceattributname',
name='replace',
field=models.CharField(blank=True, help_text='name under which the attribute will be showto the service. empty = default name of the attribut', max_length=255, verbose_name='replace'),
),
migrations.AlterField(
model_name='replaceattributvalue',
name='attribut',
field=models.CharField(help_text='Name of the attribute for which the value must be replace', max_length=255, verbose_name='attribute'),
),
migrations.AlterField(
model_name='servicepattern',
name='user_field',
field=models.CharField(blank=True, default=b'', help_text='Name of the attribute to transmit as username, empty = login', max_length=255, verbose_name='user field'),
),
]

View File

@ -1,36 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-08-24 21:12
from __future__ import unicode_literals
import cas_server.utils
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cas_server', '0009_auto_20160814_0619'),
]
operations = [
migrations.AlterField(
model_name='filterattributvalue',
name='pattern',
field=models.CharField(help_text='a regular expression', max_length=255, validators=[cas_server.utils.regexpr_validator], verbose_name='pattern'),
),
migrations.AlterField(
model_name='replaceattributname',
name='replace',
field=models.CharField(blank=True, help_text='name under which the attribute will be show to the service. empty = default name of the attribut', max_length=255, verbose_name='replace'),
),
migrations.AlterField(
model_name='replaceattributvalue',
name='pattern',
field=models.CharField(help_text='An regular expression maching whats need to be replaced', max_length=255, validators=[cas_server.utils.regexpr_validator], verbose_name='pattern'),
),
migrations.AlterField(
model_name='servicepattern',
name='pattern',
field=models.CharField(help_text="A regular expression matching services. Will usually looks like '^https://some\\.server\\.com/path/.*$'.As it is a regular expression, special character must be escaped with a '\\'.", max_length=255, unique=True, validators=[cas_server.utils.regexpr_validator], verbose_name='pattern'),
),
]

View File

@ -1,38 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2016-10-07 12:58
from __future__ import unicode_literals
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('cas_server', '0010_auto_20160824_2112'),
]
operations = [
migrations.CreateModel(
name='UserAttributes',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('_attributs', models.TextField(blank=True, default=None, null=True)),
('username', models.CharField(max_length=155, unique=True)),
],
options={
'verbose_name': 'User attributes cache',
'verbose_name_plural': 'User attributes caches',
},
),
migrations.AlterModelOptions(
name='federateduser',
options={'verbose_name': 'Federated user', 'verbose_name_plural': 'Federated users'},
),
migrations.AddField(
model_name='user',
name='last_login',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
]

View File

@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-28 14:10
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cas_server', '0011_auto_20161007_1258'),
]
operations = [
migrations.AlterField(
model_name='federatediendityprovider',
name='cas_protocol_version',
field=models.CharField(choices=[('1', 'CAS 1.0'), ('2', 'CAS 2.0'), ('3', 'CAS 3.0'), ('CAS_2_SAML_1_0', 'SAML 1.1')], default='3', help_text='Version of the CAS protocol to use when sending requests the the backend CAS.', max_length=30, verbose_name='CAS protocol version'),
),
migrations.AlterField(
model_name='servicepattern',
name='single_log_out_callback',
field=models.CharField(blank=True, default='', help_text='URL where the SLO request will be POST. empty = service url\nThis is usefull for non HTTP proxied services.', max_length=255, verbose_name='single log out callback'),
),
migrations.AlterField(
model_name='servicepattern',
name='user_field',
field=models.CharField(blank=True, default='', help_text='Name of the attribute to transmit as username, empty = login', max_length=255, verbose_name='user field'),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-29 15:48
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cas_server', '0012_auto_20170328_1610'),
]
operations = [
migrations.AlterField(
model_name='user',
name='username',
field=models.CharField(max_length=250),
),
]

View File

@ -641,7 +641,11 @@ class Username(models.Model):
#: ForeignKey to a :class:`ServicePattern`. :class:`Username` instances for a #: ForeignKey to a :class:`ServicePattern`. :class:`Username` instances for a
#: :class:`ServicePattern` are accessible thought its :attr:`ServicePattern.usernames` #: :class:`ServicePattern` are accessible thought its :attr:`ServicePattern.usernames`
#: attribute. #: attribute.
service_pattern = models.ForeignKey(ServicePattern, related_name="usernames") service_pattern = models.ForeignKey(
ServicePattern,
related_name="usernames",
on_delete=models.CASCADE
)
def __str__(self): def __str__(self):
return self.value return self.value
@ -676,7 +680,11 @@ class ReplaceAttributName(models.Model):
#: ForeignKey to a :class:`ServicePattern`. :class:`ReplaceAttributName` instances for a #: ForeignKey to a :class:`ServicePattern`. :class:`ReplaceAttributName` instances for a
#: :class:`ServicePattern` are accessible thought its :attr:`ServicePattern.attributs` #: :class:`ServicePattern` are accessible thought its :attr:`ServicePattern.attributs`
#: attribute. #: attribute.
service_pattern = models.ForeignKey(ServicePattern, related_name="attributs") service_pattern = models.ForeignKey(
ServicePattern,
related_name="attributs",
on_delete=models.CASCADE
)
def __str__(self): def __str__(self):
if not self.replace: if not self.replace:
@ -711,7 +719,11 @@ class FilterAttributValue(models.Model):
#: ForeignKey to a :class:`ServicePattern`. :class:`FilterAttributValue` instances for a #: ForeignKey to a :class:`ServicePattern`. :class:`FilterAttributValue` instances for a
#: :class:`ServicePattern` are accessible thought its :attr:`ServicePattern.filters` #: :class:`ServicePattern` are accessible thought its :attr:`ServicePattern.filters`
#: attribute. #: attribute.
service_pattern = models.ForeignKey(ServicePattern, related_name="filters") service_pattern = models.ForeignKey(
ServicePattern,
related_name="filters",
on_delete=models.CASCADE
)
def __str__(self): def __str__(self):
return u"%s %s" % (self.attribut, self.pattern) return u"%s %s" % (self.attribut, self.pattern)
@ -748,7 +760,11 @@ class ReplaceAttributValue(models.Model):
#: ForeignKey to a :class:`ServicePattern`. :class:`ReplaceAttributValue` instances for a #: ForeignKey to a :class:`ServicePattern`. :class:`ReplaceAttributValue` instances for a
#: :class:`ServicePattern` are accessible thought its :attr:`ServicePattern.replacements` #: :class:`ServicePattern` are accessible thought its :attr:`ServicePattern.replacements`
#: attribute. #: attribute.
service_pattern = models.ForeignKey(ServicePattern, related_name="replacements") service_pattern = models.ForeignKey(
ServicePattern,
related_name="replacements",
on_delete=models.CASCADE
)
def __str__(self): def __str__(self):
return u"%s %s %s" % (self.attribut, self.pattern, self.replace) return u"%s %s %s" % (self.attribut, self.pattern, self.replace)
@ -764,14 +780,18 @@ class Ticket(JsonAttributes):
class Meta: class Meta:
abstract = True abstract = True
#: ForeignKey to a :class:`User`. #: ForeignKey to a :class:`User`.
user = models.ForeignKey(User, related_name="%(class)s") user = models.ForeignKey(User, related_name="%(class)s", on_delete=models.CASCADE)
#: A boolean. ``True`` if the ticket has been validated #: A boolean. ``True`` if the ticket has been validated
validate = models.BooleanField(default=False) validate = models.BooleanField(default=False)
#: The service url for the ticket #: The service url for the ticket
service = models.TextField() service = models.TextField()
#: ForeignKey to a :class:`ServicePattern`. The :class:`ServicePattern` corresponding to #: ForeignKey to a :class:`ServicePattern`. The :class:`ServicePattern` corresponding to
#: :attr:`service`. Use :meth:`ServicePattern.validate` to find it. #: :attr:`service`. Use :meth:`ServicePattern.validate` to find it.
service_pattern = models.ForeignKey(ServicePattern, related_name="%(class)s") service_pattern = models.ForeignKey(
ServicePattern,
related_name="%(class)s",
on_delete=models.CASCADE
)
#: Date of the ticket creation #: Date of the ticket creation
creation = models.DateTimeField(auto_now_add=True) creation = models.DateTimeField(auto_now_add=True)
#: A boolean. ``True`` if the user has just renew his authentication #: A boolean. ``True`` if the user has just renew his authentication
@ -1034,7 +1054,7 @@ class Proxy(models.Model):
#: ForeignKey to a :class:`ProxyTicket`. :class:`Proxy` instances for a #: ForeignKey to a :class:`ProxyTicket`. :class:`Proxy` instances for a
#: :class:`ProxyTicket` are accessible thought its :attr:`ProxyTicket.proxies` #: :class:`ProxyTicket` are accessible thought its :attr:`ProxyTicket.proxies`
#: attribute. #: attribute.
proxy_ticket = models.ForeignKey(ProxyTicket, related_name="proxies") proxy_ticket = models.ForeignKey(ProxyTicket, related_name="proxies", on_delete=models.CASCADE)
def __str__(self): def __str__(self):
return self.url return self.url
@ -1077,7 +1097,7 @@ Upgrade using:
* pip install -U django-cas-server * pip install -U django-cas-server
* fetching the last release on * fetching the last release on
https://github.com/nitmir/django-cas-server/ or on https://github.com/nitmir/django-cas-server/ or on
https://pypi.python.org/pypi/django-cas-server https://pypi.org/project/django-cas-server/
After upgrade, do not forget to run: After upgrade, do not forget to run:
* ./manage.py migrate * ./manage.py migrate

View File

@ -71,7 +71,7 @@
<div style="clear: both;"></div> <div style="clear: both;"></div>
{% if settings.CAS_SHOW_POWERED %} {% if settings.CAS_SHOW_POWERED %}
<div id="footer"> <div id="footer">
<p><a class="text-muted" href="https://pypi.python.org/pypi/django-cas-server">django-cas-server powered</a></p> <p><a class="text-muted" href="https://pypi.org/project/django-cas-server/">django-cas-server powered</a></p>
</div> </div>
{% endif %} {% endif %}
<script src="{{settings.CAS_COMPONENT_URLS.jquery}}"></script> <script src="{{settings.CAS_COMPONENT_URLS.jquery}}"></script>
@ -94,6 +94,6 @@ discard_and_remember("#info-{{msg.name}}", "cas-info-{{msg.name}}", "{{msg.hash}
<!-- <!--
Powered by django-cas-server version {{VERSION}} Powered by django-cas-server version {{VERSION}}
Pypi: https://pypi.python.org/pypi/django-cas-server Pypi: https://pypi.org/project/django-cas-server/
github: https://github.com/nitmir/django-cas-server github: https://github.com/nitmir/django-cas-server
--> -->

View File

@ -29,6 +29,15 @@
</ConfirmationMethod> </ConfirmationMethod>
</SubjectConfirmation> </SubjectConfirmation>
</Subject> </Subject>
<Attribute AttributeName="authenticationDate" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>{{auth_date}}</AttributeValue>
</Attribute>
<Attribute AttributeName="longTermAuthenticationRequestTokenUsed" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>false</AttributeValue>{# we do not support long-term (Remember-Me) auth #}
</Attribute>
<Attribute AttributeName="isFromNewLogin" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>{{is_new_login}}</AttributeValue>
</Attribute>
{% for name, value in attributes %} <Attribute AttributeName="{{name}}" AttributeNamespace="http://www.ja-sig.org/products/cas/"> {% for name, value in attributes %} <Attribute AttributeName="{{name}}" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>{{value}}</AttributeValue> <AttributeValue>{{value}}</AttributeValue>
</Attribute> </Attribute>

View File

@ -2,8 +2,14 @@
<cas:authenticationSuccess> <cas:authenticationSuccess>
<cas:user>{{username}}</cas:user> <cas:user>{{username}}</cas:user>
<cas:attributes> <cas:attributes>
<cas:authenticationDate>{{auth_date}}</cas:authenticationDate>
<cas:longTermAuthenticationRequestTokenUsed>false</cas:longTermAuthenticationRequestTokenUsed>{# we do not support long-term (Remember-Me) auth #}
<cas:isFromNewLogin>{{is_new_login}}</cas:isFromNewLogin>
{% for key, value in attributes %} <cas:{{key}}>{{value}}</cas:{{key}}> {% for key, value in attributes %} <cas:{{key}}>{{value}}</cas:{{key}}>
{% endfor %} </cas:attributes> {% endfor %} </cas:attributes>
<cas:attribute name="authenticationDate" value="{{auth_date}}"/>
<cas:attribute name="longTermAuthenticationRequestTokenUsed" value="false"/>
<cas:attribute name="isFromNewLogin" value="{{is_new_login}}"/>
{% for key, value in attributes %} <cas:attribute name="{{key}}" value="{{value}}"/> {% for key, value in attributes %} <cas:attribute name="{{key}}" value="{{value}}"/>
{% endfor %}{% if proxyGrantingTicket %} <cas:proxyGrantingTicket>{{proxyGrantingTicket}}</cas:proxyGrantingTicket> {% endfor %}{% if proxyGrantingTicket %} <cas:proxyGrantingTicket>{{proxyGrantingTicket}}</cas:proxyGrantingTicket>
{% endif %}{% if proxies %} <cas:proxies> {% endif %}{% if proxies %} <cas:proxies>

View File

@ -30,7 +30,7 @@ class BaseServicePattern(object):
cls.service = "https://www.example.com" cls.service = "https://www.example.com"
cls.service_pattern = models.ServicePattern.objects.create( cls.service_pattern = models.ServicePattern.objects.create(
name="example", name="example",
pattern="^https://www\.example\.com(/.*)?$", pattern=r"^https://www\.example\.com(/.*)?$",
proxy=proxy, proxy=proxy,
) )
models.ReplaceAttributName.objects.create(name="*", service_pattern=cls.service_pattern) models.ReplaceAttributName.objects.create(name="*", service_pattern=cls.service_pattern)
@ -39,14 +39,14 @@ class BaseServicePattern(object):
cls.service_restrict_user_fail = "https://restrict_user_fail.example.com" cls.service_restrict_user_fail = "https://restrict_user_fail.example.com"
cls.service_pattern_restrict_user_fail = models.ServicePattern.objects.create( cls.service_pattern_restrict_user_fail = models.ServicePattern.objects.create(
name="restrict_user_fail", name="restrict_user_fail",
pattern="^https://restrict_user_fail\.example\.com(/.*)?$", pattern=r"^https://restrict_user_fail\.example\.com(/.*)?$",
restrict_users=True, restrict_users=True,
proxy=proxy, proxy=proxy,
) )
cls.service_restrict_user_success = "https://restrict_user_success.example.com" cls.service_restrict_user_success = "https://restrict_user_success.example.com"
cls.service_pattern_restrict_user_success = models.ServicePattern.objects.create( cls.service_pattern_restrict_user_success = models.ServicePattern.objects.create(
name="restrict_user_success", name="restrict_user_success",
pattern="^https://restrict_user_success\.example\.com(/.*)?$", pattern=r"^https://restrict_user_success\.example\.com(/.*)?$",
restrict_users=True, restrict_users=True,
proxy=proxy, proxy=proxy,
) )
@ -59,7 +59,7 @@ class BaseServicePattern(object):
cls.service_filter_fail = "https://filter_fail.example.com" cls.service_filter_fail = "https://filter_fail.example.com"
cls.service_pattern_filter_fail = models.ServicePattern.objects.create( cls.service_pattern_filter_fail = models.ServicePattern.objects.create(
name="filter_fail", name="filter_fail",
pattern="^https://filter_fail\.example\.com(/.*)?$", pattern=r"^https://filter_fail\.example\.com(/.*)?$",
proxy=proxy, proxy=proxy,
) )
models.FilterAttributValue.objects.create( models.FilterAttributValue.objects.create(
@ -70,7 +70,7 @@ class BaseServicePattern(object):
cls.service_filter_fail_alt = "https://filter_fail_alt.example.com" cls.service_filter_fail_alt = "https://filter_fail_alt.example.com"
cls.service_pattern_filter_fail_alt = models.ServicePattern.objects.create( cls.service_pattern_filter_fail_alt = models.ServicePattern.objects.create(
name="filter_fail_alt", name="filter_fail_alt",
pattern="^https://filter_fail_alt\.example\.com(/.*)?$", pattern=r"^https://filter_fail_alt\.example\.com(/.*)?$",
proxy=proxy, proxy=proxy,
) )
models.FilterAttributValue.objects.create( models.FilterAttributValue.objects.create(
@ -81,7 +81,7 @@ class BaseServicePattern(object):
cls.service_filter_success = "https://filter_success.example.com" cls.service_filter_success = "https://filter_success.example.com"
cls.service_pattern_filter_success = models.ServicePattern.objects.create( cls.service_pattern_filter_success = models.ServicePattern.objects.create(
name="filter_success", name="filter_success",
pattern="^https://filter_success\.example\.com(/.*)?$", pattern=r"^https://filter_success\.example\.com(/.*)?$",
proxy=proxy, proxy=proxy,
) )
models.FilterAttributValue.objects.create( models.FilterAttributValue.objects.create(
@ -94,21 +94,21 @@ class BaseServicePattern(object):
cls.service_field_needed_fail = "https://field_needed_fail.example.com" cls.service_field_needed_fail = "https://field_needed_fail.example.com"
cls.service_pattern_field_needed_fail = models.ServicePattern.objects.create( cls.service_pattern_field_needed_fail = models.ServicePattern.objects.create(
name="field_needed_fail", name="field_needed_fail",
pattern="^https://field_needed_fail\.example\.com(/.*)?$", pattern=r"^https://field_needed_fail\.example\.com(/.*)?$",
user_field="uid", user_field="uid",
proxy=proxy, proxy=proxy,
) )
cls.service_field_needed_success = "https://field_needed_success.example.com" cls.service_field_needed_success = "https://field_needed_success.example.com"
cls.service_pattern_field_needed_success = models.ServicePattern.objects.create( cls.service_pattern_field_needed_success = models.ServicePattern.objects.create(
name="field_needed_success", name="field_needed_success",
pattern="^https://field_needed_success\.example\.com(/.*)?$", pattern=r"^https://field_needed_success\.example\.com(/.*)?$",
user_field="alias", user_field="alias",
proxy=proxy, proxy=proxy,
) )
cls.service_field_needed_success_alt = "https://field_needed_success_alt.example.com" cls.service_field_needed_success_alt = "https://field_needed_success_alt.example.com"
cls.service_pattern_field_needed_success = models.ServicePattern.objects.create( cls.service_pattern_field_needed_success = models.ServicePattern.objects.create(
name="field_needed_success_alt", name="field_needed_success_alt",
pattern="^https://field_needed_success_alt\.example\.com(/.*)?$", pattern=r"^https://field_needed_success_alt\.example\.com(/.*)?$",
user_field="nom", user_field="nom",
proxy=proxy, proxy=proxy,
) )
@ -149,15 +149,25 @@ class XmlContent(object):
namespaces={'cas': "http://www.yale.edu/tp/cas"} namespaces={'cas': "http://www.yale.edu/tp/cas"}
) )
self.assertEqual(len(attributes), 1) self.assertEqual(len(attributes), 1)
ignore_attrs = {
"authenticationDate", "longTermAuthenticationRequestTokenUsed", "isFromNewLogin"
}
ignored_attrs = 0
attrs1 = set() attrs1 = set()
for attr in attributes[0]: for attr in attributes[0]:
attrs1.add((attr.tag[len("http://www.yale.edu/tp/cas")+2:], attr.text)) name = attr.tag[len("http://www.yale.edu/tp/cas")+2:]
if name not in ignore_attrs:
attrs1.add((name, attr.text))
else:
ignored_attrs += 1
attributes = root.xpath("//cas:attribute", namespaces={'cas': "http://www.yale.edu/tp/cas"}) attributes = root.xpath("//cas:attribute", namespaces={'cas': "http://www.yale.edu/tp/cas"})
self.assertEqual(len(attributes), len(attrs1)) self.assertEqual(len(attributes), len(attrs1) + ignored_attrs)
attrs2 = set() attrs2 = set()
for attr in attributes: for attr in attributes:
attrs2.add((attr.attrib['name'], attr.attrib['value'])) name = attr.attrib['name']
if name not in ignore_attrs:
attrs2.add((name, attr.attrib['value']))
original = set() original = set()
for key, value in original_attributes.items(): for key, value in original_attributes.items():
if isinstance(value, list): if isinstance(value, list):

View File

@ -46,7 +46,6 @@ MIDDLEWARE = [
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.locale.LocaleMiddleware', 'django.middleware.locale.LocaleMiddleware',

View File

@ -140,7 +140,7 @@ class UserTestCase(TestCase, UserModels):
self.service = 'http://127.0.0.1:45678' self.service = 'http://127.0.0.1:45678'
self.service_pattern = models.ServicePattern.objects.create( self.service_pattern = models.ServicePattern.objects.create(
name="localhost", name="localhost",
pattern="^https?://127\.0\.0\.1(:[0-9]+)?(/.*)?$", pattern=r"^https?://127\.0\.0\.1(:[0-9]+)?(/.*)?$",
single_log_out=True single_log_out=True
) )
models.ReplaceAttributName.objects.create(name="*", service_pattern=self.service_pattern) models.ReplaceAttributName.objects.create(name="*", service_pattern=self.service_pattern)
@ -241,7 +241,7 @@ class TicketTestCase(TestCase, UserModels, BaseServicePattern):
self.service = 'http://127.0.0.1:45678' self.service = 'http://127.0.0.1:45678'
self.service_pattern = models.ServicePattern.objects.create( self.service_pattern = models.ServicePattern.objects.create(
name="localhost", name="localhost",
pattern="^https?://127\.0\.0\.1(:[0-9]+)?(/.*)?$", pattern=r"^https?://127\.0\.0\.1(:[0-9]+)?(/.*)?$",
single_log_out=True single_log_out=True
) )
models.ReplaceAttributName.objects.create(name="*", service_pattern=self.service_pattern) models.ReplaceAttributName.objects.create(name="*", service_pattern=self.service_pattern)

View File

@ -226,7 +226,7 @@ class UtilsTestCase(TestCase):
""" """
try: try:
# first check if pypi is available # first check if pypi is available
utils.requests.get("https://pypi.python.org/simple/django-cas-server/") utils.requests.get("https://pypi.org/simple/django-cas-server/")
except utils.requests.exceptions.RequestException: except utils.requests.exceptions.RequestException:
warnings.warn( warnings.warn(
( (

View File

@ -710,7 +710,7 @@ class LogoutTestCase(TestCase):
self.service = 'http://127.0.0.1:45678' self.service = 'http://127.0.0.1:45678'
self.service_pattern = models.ServicePattern.objects.create( self.service_pattern = models.ServicePattern.objects.create(
name="localhost", name="localhost",
pattern="^https?://127\.0\.0\.1(:[0-9]+)?(/.*)?$", pattern=r"^https?://127\.0\.0\.1(:[0-9]+)?(/.*)?$",
single_log_out=True single_log_out=True
) )
# return all user attributes # return all user attributes
@ -984,7 +984,7 @@ class AuthTestCase(TestCase):
self.service = 'https://www.example.com' self.service = 'https://www.example.com'
models.ServicePattern.objects.create( models.ServicePattern.objects.create(
name="example", name="example",
pattern="^https://www\.example\.com(/.*)?$" pattern=r"^https://www\.example\.com(/.*)?$"
) )
@override_settings(CAS_AUTH_SHARED_SECRET='test') @override_settings(CAS_AUTH_SHARED_SECRET='test')
@ -1120,7 +1120,7 @@ class ValidateTestCase(TestCase):
self.service = 'https://www.example.com' self.service = 'https://www.example.com'
self.service_pattern = models.ServicePattern.objects.create( self.service_pattern = models.ServicePattern.objects.create(
name="example", name="example",
pattern="^https://www\.example\.com(/.*)?$" pattern=r"^https://www\.example\.com(/.*)?$"
) )
models.ReplaceAttributName.objects.create(name="*", service_pattern=self.service_pattern) models.ReplaceAttributName.objects.create(name="*", service_pattern=self.service_pattern)
# setting up a test service and pattern using a multi valued user attribut as username # setting up a test service and pattern using a multi valued user attribut as username
@ -1128,14 +1128,14 @@ class ValidateTestCase(TestCase):
self.service_user_field = "https://user_field.example.com" self.service_user_field = "https://user_field.example.com"
self.service_pattern_user_field = models.ServicePattern.objects.create( self.service_pattern_user_field = models.ServicePattern.objects.create(
name="user field", name="user field",
pattern="^https://user_field\.example\.com(/.*)?$", pattern=r"^https://user_field\.example\.com(/.*)?$",
user_field="alias" user_field="alias"
) )
# setting up a test service and pattern using a single valued user attribut as username # setting up a test service and pattern using a single valued user attribut as username
self.service_user_field_alt = "https://user_field_alt.example.com" self.service_user_field_alt = "https://user_field_alt.example.com"
self.service_pattern_user_field_alt = models.ServicePattern.objects.create( self.service_pattern_user_field_alt = models.ServicePattern.objects.create(
name="user field alt", name="user field alt",
pattern="^https://user_field_alt\.example\.com(/.*)?$", pattern=r"^https://user_field_alt\.example\.com(/.*)?$",
user_field="nom" user_field="nom"
) )
@ -1272,7 +1272,7 @@ class ValidateServiceTestCase(TestCase, XmlContent):
self.service = 'http://127.0.0.1:45678' self.service = 'http://127.0.0.1:45678'
self.service_pattern = models.ServicePattern.objects.create( self.service_pattern = models.ServicePattern.objects.create(
name="localhost", name="localhost",
pattern="^https?://127\.0\.0\.1(:[0-9]+)?(/.*)?$", pattern=r"^https?://127\.0\.0\.1(:[0-9]+)?(/.*)?$",
# allow to request PGT by the service # allow to request PGT by the service
proxy_callback=True, proxy_callback=True,
# allow to request PT for the service # allow to request PT for the service
@ -1285,14 +1285,14 @@ class ValidateServiceTestCase(TestCase, XmlContent):
self.service_user_field = "https://user_field.example.com" self.service_user_field = "https://user_field.example.com"
self.service_pattern_user_field = models.ServicePattern.objects.create( self.service_pattern_user_field = models.ServicePattern.objects.create(
name="user field", name="user field",
pattern="^https://user_field\.example\.com(/.*)?$", pattern=r"^https://user_field\.example\.com(/.*)?$",
user_field="alias" user_field="alias"
) )
# test service pattern using the attribute nom as username # test service pattern using the attribute nom as username
self.service_user_field_alt = "https://user_field_alt.example.com" self.service_user_field_alt = "https://user_field_alt.example.com"
self.service_pattern_user_field_alt = models.ServicePattern.objects.create( self.service_pattern_user_field_alt = models.ServicePattern.objects.create(
name="user field alt", name="user field alt",
pattern="^https://user_field_alt\.example\.com(/.*)?$", pattern=r"^https://user_field_alt\.example\.com(/.*)?$",
user_field="nom" user_field="nom"
) )
@ -1300,7 +1300,7 @@ class ValidateServiceTestCase(TestCase, XmlContent):
self.service_one_attribute = "https://one_attribute.example.com" self.service_one_attribute = "https://one_attribute.example.com"
self.service_pattern_one_attribute = models.ServicePattern.objects.create( self.service_pattern_one_attribute = models.ServicePattern.objects.create(
name="one_attribute", name="one_attribute",
pattern="^https://one_attribute\.example\.com(/.*)?$" pattern=r"^https://one_attribute\.example\.com(/.*)?$"
) )
models.ReplaceAttributName.objects.create( models.ReplaceAttributName.objects.create(
name="nom", name="nom",
@ -1311,7 +1311,7 @@ class ValidateServiceTestCase(TestCase, XmlContent):
self.service_replace_attribute_list = "https://replace_attribute_list.example.com" self.service_replace_attribute_list = "https://replace_attribute_list.example.com"
self.service_pattern_replace_attribute_list = models.ServicePattern.objects.create( self.service_pattern_replace_attribute_list = models.ServicePattern.objects.create(
name="replace_attribute_list", name="replace_attribute_list",
pattern="^https://replace_attribute_list\.example\.com(/.*)?$", pattern=r"^https://replace_attribute_list\.example\.com(/.*)?$",
) )
models.ReplaceAttributValue.objects.create( models.ReplaceAttributValue.objects.create(
attribut="alias", attribut="alias",
@ -1327,7 +1327,7 @@ class ValidateServiceTestCase(TestCase, XmlContent):
self.service_replace_attribute = "https://replace_attribute.example.com" self.service_replace_attribute = "https://replace_attribute.example.com"
self.service_pattern_replace_attribute = models.ServicePattern.objects.create( self.service_pattern_replace_attribute = models.ServicePattern.objects.create(
name="replace_attribute", name="replace_attribute",
pattern="^https://replace_attribute\.example\.com(/.*)?$", pattern=r"^https://replace_attribute\.example\.com(/.*)?$",
) )
models.ReplaceAttributValue.objects.create( models.ReplaceAttributValue.objects.create(
attribut="nom", attribut="nom",
@ -1683,7 +1683,7 @@ class ProxyTestCase(TestCase, BaseServicePattern, XmlContent):
self.service = 'http://127.0.0.1' self.service = 'http://127.0.0.1'
self.service_pattern = models.ServicePattern.objects.create( self.service_pattern = models.ServicePattern.objects.create(
name="localhost", name="localhost",
pattern="^http://127\.0\.0\.1(:[0-9]+)?(/.*)?$", pattern=r"^http://127\.0\.0\.1(:[0-9]+)?(/.*)?$",
proxy=True, proxy=True,
proxy_callback=True proxy_callback=True
) )
@ -1857,7 +1857,7 @@ class SamlValidateTestCase(TestCase, BaseServicePattern, XmlContent):
self.service_pgt = 'http://127.0.0.1' self.service_pgt = 'http://127.0.0.1'
self.service_pattern_pgt = models.ServicePattern.objects.create( self.service_pattern_pgt = models.ServicePattern.objects.create(
name="localhost", name="localhost",
pattern="^http://127\.0\.0\.1(:[0-9]+)?(/.*)?$", pattern=r"^http://127\.0\.0\.1(:[0-9]+)?(/.*)?$",
proxy=True, proxy=True,
proxy_callback=True proxy_callback=True
) )
@ -1907,9 +1907,13 @@ class SamlValidateTestCase(TestCase, BaseServicePattern, XmlContent):
"//samla:AttributeStatement/samla:Attribute", "//samla:AttributeStatement/samla:Attribute",
namespaces={'samla': "urn:oasis:names:tc:SAML:1.0:assertion"} namespaces={'samla': "urn:oasis:names:tc:SAML:1.0:assertion"}
) )
ignore_attrs = {
"authenticationDate", "longTermAuthenticationRequestTokenUsed", "isFromNewLogin"
} - set(original_attributes.keys())
attrs = set() attrs = set()
for attr in attributes: for attr in attributes:
attrs.add((attr.attrib['AttributeName'], attr.getchildren()[0].text)) if not attr.attrib['AttributeName'] in ignore_attrs:
attrs.add((attr.attrib['AttributeName'], attr.getchildren()[0].text))
original = set() original = set()
for key, value in original_attributes.items(): for key, value in original_attributes.items():
if isinstance(value, list): if isinstance(value, list):

View File

@ -263,8 +263,10 @@ class DummyCAS(BaseHTTPServer.BaseHTTPRequestHandler):
if self.test_params(): if self.test_params():
template = loader.get_template('cas_server/serviceValidate.xml') template = loader.get_template('cas_server/serviceValidate.xml')
context = Context({ context = Context({
'username': self.server.username, 'username': self.server.username.decode('utf-8'),
'attributes': self.server.attributes 'attributes': self.server.attributes,
'auth_date': timezone.now().replace(microsecond=0).isoformat(),
'is_new_login': 'true',
}) })
self.wfile.write(return_bytes(template.render(context), "utf8")) self.wfile.write(return_bytes(template.render(context), "utf8"))
else: else:
@ -299,8 +301,10 @@ class DummyCAS(BaseHTTPServer.BaseHTTPRequestHandler):
'expireInstant': (timezone.now() + timedelta(seconds=60)).isoformat(), 'expireInstant': (timezone.now() + timedelta(seconds=60)).isoformat(),
'Recipient': self.server.service, 'Recipient': self.server.service,
'ResponseID': utils.gen_saml_id(), 'ResponseID': utils.gen_saml_id(),
'username': self.server.username, 'username': self.server.username.decode('utf-8'),
'attributes': self.server.attributes, 'attributes': self.server.attributes,
'auth_date': timezone.now().replace(microsecond=0).isoformat(),
'is_new_login': 'true',
}) })
self.wfile.write(return_bytes(template.render(context), "utf8")) self.wfile.write(return_bytes(template.render(context), "utf8"))
else: else:

View File

@ -19,7 +19,10 @@ from cas_server import views
app_name = "cas_server" app_name = "cas_server"
urlpatterns = [ urlpatterns = [
url(r'^$', RedirectView.as_view(pattern_name="cas_server:login", permanent=False)), url(
r'^$',
RedirectView.as_view(pattern_name="cas_server:login", permanent=False, query_string=True)
),
url( url(
'^login$', '^login$',
sensitive_post_parameters('password')( sensitive_post_parameters('password')(

View File

@ -12,7 +12,6 @@
"""Some util function for the app""" """Some util function for the app"""
from .default_settings import settings from .default_settings import settings
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, HttpResponse from django.http import HttpResponseRedirect, HttpResponse
from django.contrib import messages from django.contrib import messages
from django.contrib.messages import constants as DEFAULT_MESSAGE_LEVELS from django.contrib.messages import constants as DEFAULT_MESSAGE_LEVELS
@ -20,6 +19,10 @@ from django.core.serializers.json import DjangoJSONEncoder
from django.utils import timezone from django.utils import timezone
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
try:
from django.urls import reverse
except ImportError:
from django.core.urlresolvers import reverse
import re import re
import random import random

View File

@ -13,7 +13,6 @@
from .default_settings import settings, SessionStore from .default_settings import settings, SessionStore
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
from django.contrib import messages from django.contrib import messages
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
@ -24,6 +23,10 @@ from django.middleware.csrf import CsrfViewMiddleware
from django.views.generic import View from django.views.generic import View
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
try:
from django.urls import reverse
except ImportError:
from django.core.urlresolvers import reverse
import re import re
import logging import logging
@ -1150,12 +1153,14 @@ class ValidateService(View):
params = { params = {
'username': self.ticket.username(), 'username': self.ticket.username(),
'attributes': self.ticket.attributs_flat(), 'attributes': self.ticket.attributs_flat(),
'proxies': proxies 'proxies': proxies,
'auth_date': self.ticket.user.last_login.replace(microsecond=0).isoformat(),
'is_new_login': 'true' if self.ticket.renew else 'false'
} }
# if pgtUrl is set, require https or localhost # if pgtUrl is set, require https or localhost
if self.pgt_url and ( if self.pgt_url and (
self.pgt_url.startswith("https://") or self.pgt_url.startswith("https://") or
re.match("^http://(127\.0\.0\.1|localhost)(:[0-9]+)?(/.*)?$", self.pgt_url) re.match(r"^http://(127\.0\.0\.1|localhost)(:[0-9]+)?(/.*)?$", self.pgt_url)
): ):
return self.process_pgturl(params) return self.process_pgturl(params)
else: else:
@ -1412,7 +1417,10 @@ class SamlValidate(CsrfExemptView):
'Recipient': self.target, 'Recipient': self.target,
'ResponseID': utils.gen_saml_id(), 'ResponseID': utils.gen_saml_id(),
'username': self.ticket.username(), 'username': self.ticket.username(),
'attributes': self.ticket.attributs_flat() 'attributes': self.ticket.attributs_flat(),
'auth_date': self.ticket.user.last_login.replace(microsecond=0).isoformat(),
'is_new_login': 'true' if self.ticket.renew else 'false'
} }
logger.info( logger.info(
"SamlValidate: ticket %s validated for user %s on service %s." % ( "SamlValidate: ticket %s validated for user %s on service %s." % (

View File

@ -1,4 +1,4 @@
Django >= 1.7.1,<1.11 Django >= 1.11,<2.1
setuptools>=5.5 setuptools>=5.5
requests>=2.4 requests>=2.4
requests_futures>=0.9.5 requests_futures>=0.9.5

View File

@ -31,10 +31,8 @@ if __name__ == '__main__':
'Environment :: Web Environment', 'Environment :: Web Environment',
'Development Status :: 5 - Production/Stable', 'Development Status :: 5 - Production/Stable',
'Framework :: Django', 'Framework :: Django',
'Framework :: Django :: 1.7', 'Framework :: Django :: 1.11',
'Framework :: Django :: 1.8', 'Framework :: Django :: 2.0',
'Framework :: Django :: 1.9',
'Framework :: Django :: 1.10',
'Intended Audience :: Developers', 'Intended Audience :: Developers',
'Intended Audience :: System Administrators', 'Intended Audience :: System Administrators',
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
@ -43,8 +41,8 @@ if __name__ == '__main__':
'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Internet :: WWW/HTTP', 'Topic :: Internet :: WWW/HTTP',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
@ -59,7 +57,7 @@ if __name__ == '__main__':
}, },
keywords=['django', 'cas', 'cas3', 'server', 'sso', 'single sign-on', 'authentication', 'auth'], keywords=['django', 'cas', 'cas3', 'server', 'sso', 'single sign-on', 'authentication', 'auth'],
install_requires=[ install_requires=[
'Django >= 1.7,<1.11', 'requests >= 2.4', 'requests_futures >= 0.9.5', 'Django >= 1.11,<2.1', 'requests >= 2.4', 'requests_futures >= 0.9.5',
'lxml >= 3.4', 'six >= 1' 'lxml >= 3.4', 'six >= 1'
], ],
url="https://github.com/nitmir/django-cas-server", url="https://github.com/nitmir/django-cas-server",

71
tox.ini
View File

@ -2,17 +2,11 @@
envlist= envlist=
flake8, flake8,
check_rst, check_rst,
py27-django17, py27-django111,
py27-django18, py35-django111,
py27-django19, py36-django111,
py27-django110, py35-django20,
py34-django17, py36-django20,
py34-django18,
py34-django19,
py34-django110,
py35-django18,
py35-django19,
py35-django110,
[flake8] [flake8]
max-line-length=100 max-line-length=100
@ -62,6 +56,12 @@ deps =
Django>=1.10,<1.11 Django>=1.10,<1.11
{[base]deps} {[base]deps}
[testenv:py27-django111]
basepython=python2.7
deps =
Django>=1.11,<1.12
{[base]deps}
[testenv:py34-django17] [testenv:py34-django17]
basepython=python3.4 basepython=python3.4
deps = deps =
@ -74,36 +74,37 @@ deps =
Django>=1.8,<1.9 Django>=1.8,<1.9
{[base]deps} {[base]deps}
[testenv:py34-django19]
basepython=python3.4
deps =
Django>=1.9,<1.10
{[base]deps}
[testenv:py34-django110]
basepython=python3.4
deps =
Django>=1.10,<1.11
{[base]deps}
[testenv:py35-django18]
basepython=python3.5
deps =
Django>=1.8,<1.9
{[base]deps}
[testenv:py35-django19]
basepython=python3.5
deps =
Django>=1.9,<1.10
{[base]deps}
[testenv:py35-django110] [testenv:py35-django110]
basepython=python3.5 basepython=python3.5
deps = deps =
Django>=1.10,<1.11 Django>=1.10,<1.11
{[base]deps} {[base]deps}
[testenv:py35-django111]
basepython=python3.5
deps =
Django>=1.11,<1.12
{[base]deps}
[testenv:py36-django111]
basepython=python3.6
deps =
Django>=1.11,<1.12
{[base]deps}
[testenv:py35-django20]
basepython=python3.5
deps =
Django>=2.0,<2.1
{[base]deps}
[testenv:py36-django20]
basepython=python3.6
deps =
Django>=2.0,<2.1
{[base]deps}
[testenv:flake8] [testenv:flake8]
basepython=python basepython=python