Merge pull request #22 from nitmir/dev
Update version to 0.8.0 Added ----- * Add a test for login with missing parameter (username or password or both) * Add ldap auth using bind method (use the user credentials to bind the the ldap server and let the server check the credentials) * Add CAS_TGT_VALIDITY parameter: Max time after with the user MUST reauthenticate. Fixed ----- * Allow both unicode and bytes dotted string in utils.import_attr * Fix some spelling and grammar on log messages. (thanks to Allie Micka) * Fix froms css class error on success/error due to a scpaless block * Disable pip cache then installing with make install Changed ------- * Update french translation
This commit is contained in:
commit
aa2a35b279
@ -2,24 +2,25 @@
|
|||||||
BASEDIR="$1"
|
BASEDIR="$1"
|
||||||
PROJECT_NAME="$2"
|
PROJECT_NAME="$2"
|
||||||
|
|
||||||
cd "$BASEDIR/htmlcov/"; tar czf "$BASEDIR/coverage.tar.gz" ./
|
TITLE="Coverage report of $PROJECT_NAME"
|
||||||
|
|
||||||
|
|
||||||
cd "$BASEDIR"
|
|
||||||
|
|
||||||
# build by gitlab CI
|
# build by gitlab CI
|
||||||
if [ -n "$CI_BUILD_REF_NAME" ]; then
|
if [ -n "$CI_BUILD_REF_NAME" ]; then
|
||||||
BRANCH="$CI_BUILD_REF_NAME"
|
BRANCH="$CI_BUILD_REF_NAME"
|
||||||
|
TITLE="$TITLE, $BRANCH branch"
|
||||||
# build by travis
|
# build by travis
|
||||||
elif [ -n "$TRAVIS_BRANCH" ]; then
|
elif [ -n "$TRAVIS_BRANCH" ]; then
|
||||||
# if this a pull request ?
|
# if this a pull request ?
|
||||||
if [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then
|
if [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then
|
||||||
BRANCH="pull-request-$TRAVIS_PULL_REQUEST"
|
BRANCH="pull-request-$TRAVIS_PULL_REQUEST"
|
||||||
|
TITLE="$TITLE, pull request n°$BRANCH"
|
||||||
else
|
else
|
||||||
BRANCH="$TRAVIS_BRANCH"
|
BRANCH="$TRAVIS_BRANCH"
|
||||||
|
TITLE="$TITLE, $BRANCH branch"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
BRANCH="$(git rev-parse --abbrev-ref HEAD)"
|
BRANCH="$(git rev-parse --abbrev-ref HEAD)"
|
||||||
|
TITLE="$TITLE, $BRANCH branch"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$BRANCH" = "HEAD" ]] || [ -z "$BRANCH" ]; then
|
if [[ "$BRANCH" = "HEAD" ]] || [ -z "$BRANCH" ]; then
|
||||||
@ -27,7 +28,23 @@ if [[ "$BRANCH" = "HEAD" ]] || [ -z "$BRANCH" ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
curl https://badges.genua.fr/local/coverage/ \
|
|
||||||
|
VENV="$(mktemp -d)"
|
||||||
|
HTMLREPORT="$(mktemp -d)"
|
||||||
|
virtualenv "$VENV"
|
||||||
|
"$VENV/bin/pip" install coverage
|
||||||
|
"$VENV/bin/coverage" html --title "$TITLE" --directory "$HTMLREPORT"
|
||||||
|
rm -rf "$VENV"
|
||||||
|
|
||||||
|
|
||||||
|
cd "$HTMLREPORT"; tar czf "$BASEDIR/coverage.tar.gz" ./
|
||||||
|
|
||||||
|
cd "$BASEDIR"
|
||||||
|
|
||||||
|
rm -rf "$HTMLREPORT"
|
||||||
|
|
||||||
|
|
||||||
|
curl https://badges.genua.fr/coverage/ \
|
||||||
-F "secret=$COVERAGE_TOKEN" \
|
-F "secret=$COVERAGE_TOKEN" \
|
||||||
-F "tar=@$BASEDIR/coverage.tar.gz" \
|
-F "tar=@$BASEDIR/coverage.tar.gz" \
|
||||||
-F "project=$PROJECT_NAME" \
|
-F "project=$PROJECT_NAME" \
|
||||||
|
@ -6,6 +6,28 @@ All notable changes to this project will be documented in this file.
|
|||||||
.. contents:: Table of Contents
|
.. contents:: Table of Contents
|
||||||
:depth: 2
|
:depth: 2
|
||||||
|
|
||||||
|
v0.8.0 - 2017-03-08
|
||||||
|
===================
|
||||||
|
|
||||||
|
Added
|
||||||
|
-----
|
||||||
|
* Add a test for login with missing parameter (username or password or both)
|
||||||
|
* Add ldap auth using bind method (use the user credentials to bind the the ldap server and let the
|
||||||
|
server check the credentials)
|
||||||
|
* Add CAS_TGT_VALIDITY parameter: Max time after with the user MUST reauthenticate.
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
-----
|
||||||
|
* Allow both unicode and bytes dotted string in utils.import_attr
|
||||||
|
* Fix some spelling and grammar on log messages. (thanks to Allie Micka)
|
||||||
|
* Fix froms css class error on success/error due to a scpaless block
|
||||||
|
* Disable pip cache then installing with make install
|
||||||
|
|
||||||
|
Changed
|
||||||
|
-------
|
||||||
|
* Update french translation
|
||||||
|
|
||||||
|
|
||||||
v0.7.4 - 2016-09-07
|
v0.7.4 - 2016-09-07
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
2
Makefile
2
Makefile
@ -6,7 +6,7 @@ build:
|
|||||||
|
|
||||||
install: dist
|
install: dist
|
||||||
pip -V
|
pip -V
|
||||||
pip install --no-deps --upgrade --force-reinstall --find-links ./dist/django-cas-server-${VERSION}.tar.gz django-cas-server
|
pip install --no-cache-dir --no-deps --upgrade --force-reinstall --find-links ./dist/django-cas-server-${VERSION}.tar.gz django-cas-server
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
pip uninstall django-cas-server || true
|
pip uninstall django-cas-server || true
|
||||||
|
21
README.rst
21
README.rst
@ -268,6 +268,11 @@ Authentication settings
|
|||||||
which inactive users are logged out. The default is ``1209600`` (2 weeks). You probably should
|
which inactive users are logged out. The default is ``1209600`` (2 weeks). You probably should
|
||||||
reduce it to something like ``86400`` seconds (1 day).
|
reduce it to something like ``86400`` seconds (1 day).
|
||||||
|
|
||||||
|
* ``CAS_TGT_VALIDITY``: Max time after with the user MUST reauthenticate. Let it to `None` for no
|
||||||
|
max time.This can be used to force refreshing cached informations only available upon user
|
||||||
|
authentication like the user attributes in federation mode or with the ldap auth in bind mode.
|
||||||
|
The default is ``None``.
|
||||||
|
|
||||||
* ``CAS_PROXY_CA_CERTIFICATE_PATH``: Path to certificate authorities file. Usually on linux
|
* ``CAS_PROXY_CA_CERTIFICATE_PATH``: Path to certificate authorities file. Usually on linux
|
||||||
the local CAs are in ``/etc/ssl/certs/ca-certificates.crt``. The default is ``True`` which
|
the local CAs are in ``/etc/ssl/certs/ca-certificates.crt``. The default is ``True`` which
|
||||||
tell requests to use its internal certificat authorities. Settings it to ``False`` should
|
tell requests to use its internal certificat authorities. Settings it to ``False`` should
|
||||||
@ -416,6 +421,14 @@ Only usefull if you are using the ldap authentication backend:
|
|||||||
The hashed password in the database is compare to the hexadecimal digest of the clear
|
The hashed password in the database is compare to the hexadecimal digest of the clear
|
||||||
password hashed with the corresponding algorithm.
|
password hashed with the corresponding algorithm.
|
||||||
* ``"plain"``, the password in the database must be in clear.
|
* ``"plain"``, the password in the database must be in clear.
|
||||||
|
* ``"bind``, the user credentials are used to bind to the ldap database and retreive the user
|
||||||
|
attribute. In this mode, the settings ``CAS_LDAP_PASSWORD_ATTR`` and ``CAS_LDAP_PASSWORD_CHARSET``
|
||||||
|
are ignored, and it is the ldap server that perform password check. The counterpart is that
|
||||||
|
the user attributes are only available upon user password check and so are cached for later
|
||||||
|
use. All the other modes directly fetch the user attributes from the database whenever there
|
||||||
|
are needed. This mean that is you use this mode, they can be some difference between the
|
||||||
|
attributes in database and the cached ones if changes happend in the database after the user
|
||||||
|
authentiate. See the parameter ``CAS_TGT_VALIDITY`` to force user to reauthenticate periodically.
|
||||||
|
|
||||||
The default is ``"ldap"``.
|
The default is ``"ldap"``.
|
||||||
* ``CAS_LDAP_PASSWORD_CHARSET``: Charset the LDAP users passwords was hash with. This is needed to
|
* ``CAS_LDAP_PASSWORD_CHARSET``: Charset the LDAP users passwords was hash with. This is needed to
|
||||||
@ -585,6 +598,10 @@ to the provider CAS to authenticate. This provider transmit to ``django-cas-serv
|
|||||||
username and attributes. The user is now logged in on ``django-cas-server`` and can use
|
username and attributes. The user is now logged in on ``django-cas-server`` and can use
|
||||||
services using ``django-cas-server`` as CAS.
|
services using ``django-cas-server`` as CAS.
|
||||||
|
|
||||||
|
In federation mode, the user attributes are cached upon user authentication. See the settings
|
||||||
|
``CAS_TGT_VALIDITY`` to force users to reauthenticate periodically and allow ``django-cas-server``
|
||||||
|
to refresh cached attributes.
|
||||||
|
|
||||||
The list of allowed identity providers is defined using the django admin application.
|
The list of allowed identity providers is defined using the django admin application.
|
||||||
With the development server started, visit http://127.0.0.1:8000/admin/ to add identity providers.
|
With the development server started, visit http://127.0.0.1:8000/admin/ to add identity providers.
|
||||||
|
|
||||||
@ -638,8 +655,8 @@ You could for example do as bellow::
|
|||||||
.. |codacy| image:: https://badges.genua.fr/codacy/grade/255c21623d6946ef8802fa7995b61366/master.svg
|
.. |codacy| image:: https://badges.genua.fr/codacy/grade/255c21623d6946ef8802fa7995b61366/master.svg
|
||||||
:target: https://www.codacy.com/app/valentin-samir/django-cas-server
|
:target: https://www.codacy.com/app/valentin-samir/django-cas-server
|
||||||
|
|
||||||
.. |coverage| image:: https://badges.genua.fr/local/coverage/?project=django-cas-server&branch=master
|
.. |coverage| image:: https://intranet.genua.fr/coverage/badge/django-cas-server/master.svg
|
||||||
:target: https://badges.genua.fr/local/coverage/django-cas-server/master
|
:target: https://badges.genua.fr/coverage/django-cas-server/master
|
||||||
|
|
||||||
.. |doc| image:: https://badges.genua.fr/local/readthedocs/?version=latest
|
.. |doc| image:: https://badges.genua.fr/local/readthedocs/?version=latest
|
||||||
:target: http://django-cas-server.readthedocs.io
|
:target: http://django-cas-server.readthedocs.io
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"""A django CAS server application"""
|
"""A django CAS server application"""
|
||||||
|
|
||||||
#: version of the application
|
#: version of the application
|
||||||
VERSION = '0.7.4'
|
VERSION = '0.8.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'
|
||||||
|
@ -9,10 +9,12 @@
|
|||||||
#
|
#
|
||||||
# (c) 2015-2016 Valentin Samir
|
# (c) 2015-2016 Valentin Samir
|
||||||
"""module for the admin interface of the app"""
|
"""module for the admin interface of the app"""
|
||||||
|
from .default_settings import settings
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import ServiceTicket, ProxyTicket, ProxyGrantingTicket, User, ServicePattern
|
from .models import ServiceTicket, ProxyTicket, ProxyGrantingTicket, User, ServicePattern
|
||||||
from .models import Username, ReplaceAttributName, ReplaceAttributValue, FilterAttributValue
|
from .models import Username, ReplaceAttributName, ReplaceAttributValue, FilterAttributValue
|
||||||
from .models import FederatedIendityProvider
|
from .models import FederatedIendityProvider, FederatedUser, UserAttributes
|
||||||
from .forms import TicketForm
|
from .forms import TicketForm
|
||||||
|
|
||||||
|
|
||||||
@ -167,6 +169,33 @@ class FederatedIendityProviderAdmin(admin.ModelAdmin):
|
|||||||
list_display = ('verbose_name', 'suffix', 'display')
|
list_display = ('verbose_name', 'suffix', 'display')
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(User, UserAdmin)
|
class FederatedUserAdmin(admin.ModelAdmin):
|
||||||
|
"""
|
||||||
|
Bases: :class:`django.contrib.admin.ModelAdmin`
|
||||||
|
|
||||||
|
:class:`FederatedUser<cas_server.models.FederatedUser>` in admin
|
||||||
|
interface
|
||||||
|
"""
|
||||||
|
#: Fields to display on a object.
|
||||||
|
fields = ('username', 'provider', 'last_update')
|
||||||
|
#: Fields to display on the list of class:`FederatedUserAdmin` objects.
|
||||||
|
list_display = ('username', 'provider', 'last_update')
|
||||||
|
|
||||||
|
|
||||||
|
class UserAttributesAdmin(admin.ModelAdmin):
|
||||||
|
"""
|
||||||
|
Bases: :class:`django.contrib.admin.ModelAdmin`
|
||||||
|
|
||||||
|
:class:`UserAttributes<cas_server.models.UserAttributes>` in admin
|
||||||
|
interface
|
||||||
|
"""
|
||||||
|
#: Fields to display on a object.
|
||||||
|
fields = ('username', '_attributs')
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(ServicePattern, ServicePatternAdmin)
|
admin.site.register(ServicePattern, ServicePatternAdmin)
|
||||||
admin.site.register(FederatedIendityProvider, FederatedIendityProviderAdmin)
|
admin.site.register(FederatedIendityProvider, FederatedIendityProviderAdmin)
|
||||||
|
if settings.DEBUG: # pragma: no branch (we always test with DEBUG True)
|
||||||
|
admin.site.register(User, UserAdmin)
|
||||||
|
admin.site.register(FederatedUser, FederatedUserAdmin)
|
||||||
|
admin.site.register(UserAttributes, UserAttributesAdmin)
|
||||||
|
@ -30,7 +30,7 @@ try: # pragma: no cover
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
ldap3 = None
|
ldap3 = None
|
||||||
|
|
||||||
from .models import FederatedUser
|
from .models import FederatedUser, UserAttributes
|
||||||
from .utils import check_password, dictfetchall
|
from .utils import check_password, dictfetchall
|
||||||
|
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ class AuthUser(object):
|
|||||||
|
|
||||||
def test_password(self, password):
|
def test_password(self, password):
|
||||||
"""
|
"""
|
||||||
Tests ``password`` agains the user password.
|
Tests ``password`` against the user-supplied password.
|
||||||
|
|
||||||
:raises NotImplementedError: always. The method need to be implemented by subclasses
|
:raises NotImplementedError: always. The method need to be implemented by subclasses
|
||||||
"""
|
"""
|
||||||
@ -74,7 +74,7 @@ class DummyAuthUser(AuthUser): # pragma: no cover
|
|||||||
|
|
||||||
def test_password(self, password):
|
def test_password(self, password):
|
||||||
"""
|
"""
|
||||||
Tests ``password`` agains the user password.
|
Tests ``password`` against the user-supplied password.
|
||||||
|
|
||||||
:param unicode password: a clear text password as submited by the user.
|
:param unicode password: a clear text password as submited by the user.
|
||||||
:return: always ``False``
|
:return: always ``False``
|
||||||
@ -102,7 +102,7 @@ class TestAuthUser(AuthUser):
|
|||||||
|
|
||||||
def test_password(self, password):
|
def test_password(self, password):
|
||||||
"""
|
"""
|
||||||
Tests ``password`` agains the user password.
|
Tests ``password`` against the user-supplied password.
|
||||||
|
|
||||||
:param unicode password: a clear text password as submited by the user.
|
:param unicode password: a clear text password as submited by the user.
|
||||||
:return: ``True`` if :attr:`username<AuthUser.username>` is valid and
|
:return: ``True`` if :attr:`username<AuthUser.username>` is valid and
|
||||||
@ -149,7 +149,7 @@ class MysqlAuthUser(DBAuthUser): # pragma: no cover
|
|||||||
"""
|
"""
|
||||||
DEPRECATED, use :class:`SqlAuthUser` instead.
|
DEPRECATED, use :class:`SqlAuthUser` instead.
|
||||||
|
|
||||||
A mysql authentication class: authenticate user agains a mysql database
|
A mysql authentication class: authenticate user against a mysql database
|
||||||
|
|
||||||
:param unicode username: A username, stored in the :attr:`username<AuthUser.username>`
|
:param unicode username: A username, stored in the :attr:`username<AuthUser.username>`
|
||||||
class attribute. Valid value are fetched from the MySQL database set with
|
class attribute. Valid value are fetched from the MySQL database set with
|
||||||
@ -188,7 +188,7 @@ class MysqlAuthUser(DBAuthUser): # pragma: no cover
|
|||||||
|
|
||||||
def test_password(self, password):
|
def test_password(self, password):
|
||||||
"""
|
"""
|
||||||
Tests ``password`` agains the user password.
|
Tests ``password`` against the user-supplied password.
|
||||||
|
|
||||||
:param unicode password: a clear text password as submited by the user.
|
:param unicode password: a clear text password as submited by the user.
|
||||||
:return: ``True`` if :attr:`username<AuthUser.username>` is valid and ``password`` is
|
:return: ``True`` if :attr:`username<AuthUser.username>` is valid and ``password`` is
|
||||||
@ -208,7 +208,7 @@ class MysqlAuthUser(DBAuthUser): # pragma: no cover
|
|||||||
|
|
||||||
class SqlAuthUser(DBAuthUser): # pragma: no cover
|
class SqlAuthUser(DBAuthUser): # pragma: no cover
|
||||||
"""
|
"""
|
||||||
A SQL authentication class: authenticate user agains a SQL database. The SQL database
|
A SQL authentication class: authenticate user against a SQL database. The SQL database
|
||||||
must be configures in settings.py as ``settings.DATABASES['cas_server']``.
|
must be configures in settings.py as ``settings.DATABASES['cas_server']``.
|
||||||
|
|
||||||
:param unicode username: A username, stored in the :attr:`username<AuthUser.username>`
|
:param unicode username: A username, stored in the :attr:`username<AuthUser.username>`
|
||||||
@ -238,7 +238,7 @@ class SqlAuthUser(DBAuthUser): # pragma: no cover
|
|||||||
|
|
||||||
def test_password(self, password):
|
def test_password(self, password):
|
||||||
"""
|
"""
|
||||||
Tests ``password`` agains the user password.
|
Tests ``password`` against the user-supplied password.
|
||||||
|
|
||||||
:param unicode password: a clear text password as submited by the user.
|
:param unicode password: a clear text password as submited by the user.
|
||||||
:return: ``True`` if :attr:`username<AuthUser.username>` is valid and ``password`` is
|
:return: ``True`` if :attr:`username<AuthUser.username>` is valid and ``password`` is
|
||||||
@ -284,6 +284,10 @@ class LdapAuthUser(DBAuthUser): # pragma: no cover
|
|||||||
def __init__(self, username):
|
def __init__(self, username):
|
||||||
if not ldap3:
|
if not ldap3:
|
||||||
raise RuntimeError("Please install ldap3 before using the LdapAuthUser backend")
|
raise RuntimeError("Please install ldap3 before using the LdapAuthUser backend")
|
||||||
|
if not settings.CAS_LDAP_BASE_DN:
|
||||||
|
raise ValueError(
|
||||||
|
"You must define CAS_LDAP_BASE_DN for using the ldap authentication backend"
|
||||||
|
)
|
||||||
# in case we got deconnected from the database, retry to connect 2 times
|
# in case we got deconnected from the database, retry to connect 2 times
|
||||||
for retry_nb in range(3):
|
for retry_nb in range(3):
|
||||||
try:
|
try:
|
||||||
@ -294,6 +298,8 @@ class LdapAuthUser(DBAuthUser): # pragma: no cover
|
|||||||
attributes=ldap3.ALL_ATTRIBUTES
|
attributes=ldap3.ALL_ATTRIBUTES
|
||||||
) and len(conn.entries) == 1:
|
) and len(conn.entries) == 1:
|
||||||
user = conn.entries[0].entry_get_attributes_dict()
|
user = conn.entries[0].entry_get_attributes_dict()
|
||||||
|
# store the user dn
|
||||||
|
user["dn"] = conn.entries[0].entry_get_dn()
|
||||||
if user.get(settings.CAS_LDAP_USERNAME_ATTR):
|
if user.get(settings.CAS_LDAP_USERNAME_ATTR):
|
||||||
self.user = user
|
self.user = user
|
||||||
super(LdapAuthUser, self).__init__(user[settings.CAS_LDAP_USERNAME_ATTR][0])
|
super(LdapAuthUser, self).__init__(user[settings.CAS_LDAP_USERNAME_ATTR][0])
|
||||||
@ -308,14 +314,41 @@ class LdapAuthUser(DBAuthUser): # pragma: no cover
|
|||||||
|
|
||||||
def test_password(self, password):
|
def test_password(self, password):
|
||||||
"""
|
"""
|
||||||
Tests ``password`` agains the user password.
|
Tests ``password`` against the user-supplied password.
|
||||||
|
|
||||||
:param unicode password: a clear text password as submited by the user.
|
:param unicode password: a clear text password as submited by the user.
|
||||||
:return: ``True`` if :attr:`username<AuthUser.username>` is valid and ``password`` is
|
:return: ``True`` if :attr:`username<AuthUser.username>` is valid and ``password`` is
|
||||||
correct, ``False`` otherwise.
|
correct, ``False`` otherwise.
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
if self.user and self.user.get(settings.CAS_LDAP_PASSWORD_ATTR):
|
if settings.CAS_LDAP_PASSWORD_CHECK == "bind":
|
||||||
|
try:
|
||||||
|
conn = ldap3.Connection(
|
||||||
|
settings.CAS_LDAP_SERVER,
|
||||||
|
self.user["dn"],
|
||||||
|
password,
|
||||||
|
auto_bind=True
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
# fetch the user attribute
|
||||||
|
if conn.search(
|
||||||
|
settings.CAS_LDAP_BASE_DN,
|
||||||
|
settings.CAS_LDAP_USER_QUERY % ldap3.utils.conv.escape_bytes(self.username),
|
||||||
|
attributes=ldap3.ALL_ATTRIBUTES
|
||||||
|
) and len(conn.entries) == 1:
|
||||||
|
attributes = conn.entries[0].entry_get_attributes_dict()
|
||||||
|
attributes["dn"] = conn.entries[0].entry_get_dn()
|
||||||
|
# cache the attributes locally as we wont have access to the user password
|
||||||
|
# later.
|
||||||
|
user = UserAttributes.objects.get_or_create(username=self.username)[0]
|
||||||
|
user.attributs = attributes
|
||||||
|
user.save()
|
||||||
|
finally:
|
||||||
|
conn.unbind()
|
||||||
|
return True
|
||||||
|
except (ldap3.LDAPBindError, ldap3.LDAPCommunicationError):
|
||||||
|
return False
|
||||||
|
elif self.user and self.user.get(settings.CAS_LDAP_PASSWORD_ATTR):
|
||||||
return check_password(
|
return check_password(
|
||||||
settings.CAS_LDAP_PASSWORD_CHECK,
|
settings.CAS_LDAP_PASSWORD_CHECK,
|
||||||
password,
|
password,
|
||||||
@ -325,6 +358,22 @@ class LdapAuthUser(DBAuthUser): # pragma: no cover
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def attributs(self):
|
||||||
|
"""
|
||||||
|
The user attributes.
|
||||||
|
|
||||||
|
:return: a :class:`dict` with the user attributes. Attributes may be :func:`unicode`
|
||||||
|
or :class:`list` of :func:`unicode`. If the user do not exists, the returned
|
||||||
|
:class:`dict` is empty.
|
||||||
|
:rtype: dict
|
||||||
|
:raises NotImplementedError: if the password check method in `CAS_LDAP_PASSWORD_CHECK`
|
||||||
|
do not allow to fetch the attributes without the user credentials.
|
||||||
|
"""
|
||||||
|
if settings.CAS_LDAP_PASSWORD_CHECK == "bind":
|
||||||
|
raise NotImplementedError()
|
||||||
|
else:
|
||||||
|
return super(LdapAuthUser, self).attributs()
|
||||||
|
|
||||||
|
|
||||||
class DjangoAuthUser(AuthUser): # pragma: no cover
|
class DjangoAuthUser(AuthUser): # pragma: no cover
|
||||||
"""
|
"""
|
||||||
@ -347,7 +396,7 @@ class DjangoAuthUser(AuthUser): # pragma: no cover
|
|||||||
|
|
||||||
def test_password(self, password):
|
def test_password(self, password):
|
||||||
"""
|
"""
|
||||||
Tests ``password`` agains the user password.
|
Tests ``password`` against the user-supplied password.
|
||||||
|
|
||||||
:param unicode password: a clear text password as submited by the user.
|
:param unicode password: a clear text password as submited by the user.
|
||||||
:return: ``True`` if :attr:`user` is valid and ``password`` is
|
:return: ``True`` if :attr:`user` is valid and ``password`` is
|
||||||
@ -426,7 +475,7 @@ class CASFederateAuth(AuthUser):
|
|||||||
|
|
||||||
def test_password(self, ticket):
|
def test_password(self, ticket):
|
||||||
"""
|
"""
|
||||||
Tests ``password`` agains the user password.
|
Tests ``password`` against the user-supplied password.
|
||||||
|
|
||||||
:param unicode password: The CAS tickets just used to validate the user authentication
|
:param unicode password: The CAS tickets just used to validate the user authentication
|
||||||
against its CAS backend.
|
against its CAS backend.
|
||||||
|
@ -58,6 +58,10 @@ CAS_SLO_MAX_PARALLEL_REQUESTS = 10
|
|||||||
CAS_SLO_TIMEOUT = 5
|
CAS_SLO_TIMEOUT = 5
|
||||||
#: Shared to transmit then using the view :class:`cas_server.views.Auth`
|
#: Shared to transmit then using the view :class:`cas_server.views.Auth`
|
||||||
CAS_AUTH_SHARED_SECRET = ''
|
CAS_AUTH_SHARED_SECRET = ''
|
||||||
|
#: Max time after with the user MUST reauthenticate. Let it to `None` for no max time.
|
||||||
|
#: This can be used to force refreshing cached informations only available upon user authentication
|
||||||
|
#: like the user attributes in federation mode or with the ldap auth in bind mode.
|
||||||
|
CAS_TGT_VALIDITY = None
|
||||||
|
|
||||||
|
|
||||||
#: Number of seconds the service tickets and proxy tickets are valid. This is the maximal time
|
#: Number of seconds the service tickets and proxy tickets are valid. This is the maximal time
|
||||||
|
@ -69,7 +69,7 @@ class CASFederateValidateUser(object):
|
|||||||
|
|
||||||
def verify_ticket(self, ticket):
|
def verify_ticket(self, ticket):
|
||||||
"""
|
"""
|
||||||
test ``ticket`` agains the CAS provider, if valid, create a
|
test ``ticket`` against the CAS provider, if valid, create a
|
||||||
:class:`FederatedUser<cas_server.models.FederatedUser>` matching provider returned
|
:class:`FederatedUser<cas_server.models.FederatedUser>` matching provider returned
|
||||||
username and attributes.
|
username and attributes.
|
||||||
|
|
||||||
|
Binary file not shown.
@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: cas_server\n"
|
"Project-Id-Version: cas_server\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2016-08-24 17:18+0200\n"
|
"POT-Creation-Date: 2016-09-18 11:29+0200\n"
|
||||||
"PO-Revision-Date: 2016-08-24 17:18+0200\n"
|
"PO-Revision-Date: 2016-09-18 11:30+0200\n"
|
||||||
"Last-Translator: Valentin Samir <valentin.samir@crans.org>\n"
|
"Last-Translator: Valentin Samir <valentin.samir@crans.org>\n"
|
||||||
"Language-Team: django <LL@li.org>\n"
|
"Language-Team: django <LL@li.org>\n"
|
||||||
"Language: fr\n"
|
"Language: fr\n"
|
||||||
@ -16,7 +16,7 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
"X-Generator: Poedit 1.8.8\n"
|
"X-Generator: Poedit 1.8.9\n"
|
||||||
|
|
||||||
#: apps.py:25 templates/cas_server/base.html:7
|
#: apps.py:25 templates/cas_server/base.html:7
|
||||||
#: templates/cas_server/base.html:26
|
#: templates/cas_server/base.html:26
|
||||||
@ -34,37 +34,37 @@ msgstr ""
|
|||||||
"identifiant et votre mot de passe chaque fois que vous changez de site, "
|
"identifiant et votre mot de passe chaque fois que vous changez de site, "
|
||||||
"jusqu'à ce que votre session expire ou que vous vous déconnectiez."
|
"jusqu'à ce que votre session expire ou que vous vous déconnectiez."
|
||||||
|
|
||||||
#: forms.py:84
|
#: forms.py:85
|
||||||
msgid "Identity provider"
|
msgid "Identity provider"
|
||||||
msgstr "fournisseur d'identité"
|
msgstr "fournisseur d'identité"
|
||||||
|
|
||||||
#: forms.py:88 forms.py:107
|
#: forms.py:89 forms.py:111
|
||||||
msgid "Warn me before logging me into other sites."
|
msgid "Warn me before logging me into other sites."
|
||||||
msgstr "Prévenez-moi avant d'accéder à d'autres services."
|
msgstr "Prévenez-moi avant d'accéder à d'autres services."
|
||||||
|
|
||||||
#: forms.py:92
|
#: forms.py:93
|
||||||
msgid "Remember the identity provider"
|
msgid "Remember the identity provider"
|
||||||
msgstr "Se souvenir du fournisseur d'identité"
|
msgstr "Se souvenir du fournisseur d'identité"
|
||||||
|
|
||||||
#: forms.py:102 models.py:594
|
#: forms.py:104 models.py:594
|
||||||
msgid "username"
|
msgid "username"
|
||||||
msgstr "nom d'utilisateur"
|
msgstr "nom d'utilisateur"
|
||||||
|
|
||||||
#: forms.py:104
|
#: forms.py:108
|
||||||
msgid "password"
|
msgid "password"
|
||||||
msgstr "mot de passe"
|
msgstr "mot de passe"
|
||||||
|
|
||||||
#: forms.py:126
|
#: forms.py:131
|
||||||
msgid "The credentials you provided cannot be determined to be authentic."
|
msgid "The credentials you provided cannot be determined to be authentic."
|
||||||
msgstr "Les informations transmises n'ont pas permis de vous authentifier."
|
msgstr "Les informations transmises n'ont pas permis de vous authentifier."
|
||||||
|
|
||||||
#: forms.py:178
|
#: forms.py:183
|
||||||
msgid "User not found in the temporary database, please try to reconnect"
|
msgid "User not found in the temporary database, please try to reconnect"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Utilisateur non trouvé dans la base de donnée temporaire, essayez de vous "
|
"Utilisateur non trouvé dans la base de donnée temporaire, essayez de vous "
|
||||||
"reconnecter"
|
"reconnecter"
|
||||||
|
|
||||||
#: forms.py:192
|
#: forms.py:197
|
||||||
msgid "service"
|
msgid "service"
|
||||||
msgstr "service"
|
msgstr "service"
|
||||||
|
|
||||||
@ -331,7 +331,7 @@ msgstr "Connexion"
|
|||||||
msgid "Connect to the service"
|
msgid "Connect to the service"
|
||||||
msgstr "Se connecter au service"
|
msgstr "Se connecter au service"
|
||||||
|
|
||||||
#: utils.py:736
|
#: utils.py:744
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "\"%(value)s\" is not a valid regular expression"
|
msgid "\"%(value)s\" is not a valid regular expression"
|
||||||
msgstr "\"%(value)s\" n'est pas une expression rationnelle valide"
|
msgstr "\"%(value)s\" n'est pas une expression rationnelle valide"
|
||||||
@ -339,7 +339,7 @@ msgstr "\"%(value)s\" n'est pas une expression rationnelle valide"
|
|||||||
#: views.py:185
|
#: views.py:185
|
||||||
msgid ""
|
msgid ""
|
||||||
"<h3>Logout successful</h3>You have successfully logged out from the Central "
|
"<h3>Logout successful</h3>You have successfully logged out from the Central "
|
||||||
"Authentication Service. For security reasons, exit your web browser."
|
"Authentication Service. For security reasons, close your web browser."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"<h3>Déconnexion réussie</h3>Vous vous êtes déconnecté(e) du Service Central "
|
"<h3>Déconnexion réussie</h3>Vous vous êtes déconnecté(e) du Service Central "
|
||||||
"d'Authentification. Pour des raisons de sécurité, veuillez fermer votre "
|
"d'Authentification. Pour des raisons de sécurité, veuillez fermer votre "
|
||||||
@ -349,7 +349,7 @@ msgstr ""
|
|||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"<h3>Logout successful</h3>You have successfully logged out from %s sessions "
|
"<h3>Logout successful</h3>You have successfully logged out from %s sessions "
|
||||||
"of the Central Authentication Service. For security reasons, exit your web "
|
"of the Central Authentication Service. For security reasons, close your web "
|
||||||
"browser."
|
"browser."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"<h3>Déconnexion réussie</h3>Vous vous êtes déconnecté(e) de %s sessions du "
|
"<h3>Déconnexion réussie</h3>Vous vous êtes déconnecté(e) de %s sessions du "
|
||||||
@ -359,7 +359,7 @@ msgstr ""
|
|||||||
#: views.py:198
|
#: views.py:198
|
||||||
msgid ""
|
msgid ""
|
||||||
"<h3>Logout successful</h3>You were already logged out from the Central "
|
"<h3>Logout successful</h3>You were already logged out from the Central "
|
||||||
"Authentication Service. For security reasons, exit your web browser."
|
"Authentication Service. For security reasons, close your web browser."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"<h3>Déconnexion réussie</h3>Vous étiez déjà déconnecté(e) du Service Central "
|
"<h3>Déconnexion réussie</h3>Vous étiez déjà déconnecté(e) du Service Central "
|
||||||
"d'Authentification. Pour des raisons de sécurité, veuillez fermer votre "
|
"d'Authentification. Pour des raisons de sécurité, veuillez fermer votre "
|
||||||
@ -375,7 +375,7 @@ msgstr ""
|
|||||||
"ticket %(ticket)s: %(error)r"
|
"ticket %(ticket)s: %(error)r"
|
||||||
|
|
||||||
#: views.py:500
|
#: views.py:500
|
||||||
msgid "Invalid login ticket, please retry to login"
|
msgid "Invalid login ticket, please try to log in again"
|
||||||
msgstr "Ticket de connexion invalide, merci de réessayé de vous connecter"
|
msgstr "Ticket de connexion invalide, merci de réessayé de vous connecter"
|
||||||
|
|
||||||
#: views.py:692
|
#: views.py:692
|
||||||
|
@ -23,4 +23,5 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
models.User.clean_deleted_sessions()
|
models.User.clean_deleted_sessions()
|
||||||
|
models.UserAttributes.clean_old_entries()
|
||||||
models.NewVersionWarning.send_mails()
|
models.NewVersionWarning.send_mails()
|
||||||
|
38
cas_server/migrations/0011_auto_20161007_1258.py
Normal file
38
cas_server/migrations/0011_auto_20161007_1258.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# -*- 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,
|
||||||
|
),
|
||||||
|
]
|
@ -163,6 +163,8 @@ class FederatedUser(JsonAttributes):
|
|||||||
"""
|
"""
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("username", "provider")
|
unique_together = ("username", "provider")
|
||||||
|
verbose_name = _("Federated user")
|
||||||
|
verbose_name_plural = _("Federated users")
|
||||||
#: The user username returned by the CAS backend on successful ticket validation
|
#: The user username returned by the CAS backend on successful ticket validation
|
||||||
username = models.CharField(max_length=124)
|
username = models.CharField(max_length=124)
|
||||||
#: A foreign key to :class:`FederatedIendityProvider`
|
#: A foreign key to :class:`FederatedIendityProvider`
|
||||||
@ -233,6 +235,30 @@ class FederateSLO(models.Model):
|
|||||||
federate_slo.delete()
|
federate_slo.delete()
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
|
class UserAttributes(JsonAttributes):
|
||||||
|
"""
|
||||||
|
Bases: :class:`JsonAttributes`
|
||||||
|
|
||||||
|
Local cache of the user attributes, used then needed
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("User attributes cache")
|
||||||
|
verbose_name_plural = _("User attributes caches")
|
||||||
|
#: The username of the user for which we cache attributes
|
||||||
|
username = models.CharField(max_length=155, unique=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.username
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clean_old_entries(cls):
|
||||||
|
"""Remove :class:`UserAttributes` for which no more :class:`User` exists."""
|
||||||
|
for user in cls.objects.all():
|
||||||
|
if User.objects.filter(username=user.username).count() == 0:
|
||||||
|
user.delete()
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class User(models.Model):
|
class User(models.Model):
|
||||||
"""
|
"""
|
||||||
@ -250,6 +276,8 @@ class User(models.Model):
|
|||||||
username = models.CharField(max_length=30)
|
username = models.CharField(max_length=30)
|
||||||
#: Last time the authenticated user has do something (auth, fetch ticket, etc…)
|
#: Last time the authenticated user has do something (auth, fetch ticket, etc…)
|
||||||
date = models.DateTimeField(auto_now=True)
|
date = models.DateTimeField(auto_now=True)
|
||||||
|
#: last time the user logged
|
||||||
|
last_login = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -269,9 +297,12 @@ class User(models.Model):
|
|||||||
Remove :class:`User` objects inactive since more that
|
Remove :class:`User` objects inactive since more that
|
||||||
:django:setting:`SESSION_COOKIE_AGE` and send corresponding SingleLogOut requests.
|
:django:setting:`SESSION_COOKIE_AGE` and send corresponding SingleLogOut requests.
|
||||||
"""
|
"""
|
||||||
users = cls.objects.filter(
|
filter = Q(date__lt=(timezone.now() - timedelta(seconds=settings.SESSION_COOKIE_AGE)))
|
||||||
date__lt=(timezone.now() - timedelta(seconds=settings.SESSION_COOKIE_AGE))
|
if settings.CAS_TGT_VALIDITY is not None:
|
||||||
|
filter |= Q(
|
||||||
|
last_login__lt=(timezone.now() - timedelta(seconds=settings.CAS_TGT_VALIDITY))
|
||||||
)
|
)
|
||||||
|
users = cls.objects.filter(filter)
|
||||||
for user in users:
|
for user in users:
|
||||||
user.logout()
|
user.logout()
|
||||||
users.delete()
|
users.delete()
|
||||||
@ -288,9 +319,22 @@ class User(models.Model):
|
|||||||
def attributs(self):
|
def attributs(self):
|
||||||
"""
|
"""
|
||||||
Property.
|
Property.
|
||||||
A fresh :class:`dict` for the user attributes, using ``settings.CAS_AUTH_CLASS``
|
A fresh :class:`dict` for the user attributes, using ``settings.CAS_AUTH_CLASS`` if
|
||||||
|
possible, and if not, try to fallback to cached attributes (actually only used for ldap
|
||||||
|
auth class with bind password check mthode).
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
return utils.import_attr(settings.CAS_AUTH_CLASS)(self.username).attributs()
|
return utils.import_attr(settings.CAS_AUTH_CLASS)(self.username).attributs()
|
||||||
|
except NotImplementedError:
|
||||||
|
try:
|
||||||
|
user = UserAttributes.objects.get(username=self.username)
|
||||||
|
attributes = user.attributs
|
||||||
|
if attributes is not None:
|
||||||
|
return attributes
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
except UserAttributes.DoesNotExist:
|
||||||
|
return {}
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return u"%s - %s" % (self.username, self.session_key)
|
return u"%s - %s" % (self.username, self.session_key)
|
||||||
@ -433,7 +477,7 @@ class ServicePattern(models.Model):
|
|||||||
"""
|
"""
|
||||||
Bases: :class:`django.db.models.Model`
|
Bases: :class:`django.db.models.Model`
|
||||||
|
|
||||||
Allowed services pattern agains services are tested to
|
Allowed services pattern against services are tested to
|
||||||
"""
|
"""
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ("pos", )
|
ordering = ("pos", )
|
||||||
|
@ -6,13 +6,13 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for field in form %}{% if not field|is_hidden %}
|
{% for field in form %}{% if not field|is_hidden %}
|
||||||
<div class="form-group{% spaceless %}
|
<div class="form-group
|
||||||
{% if not form.non_field_errors %}
|
{% if not form.non_field_errors %}
|
||||||
{% if field.errors %} has-error
|
{% if field.errors %} has-error
|
||||||
{% elif form.cleaned_data %} has-success
|
{% elif form.cleaned_data %} has-success
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}"
|
{% endif %}"
|
||||||
{% endspaceless %}>{% spaceless %}
|
>{% spaceless %}
|
||||||
{% if field|is_checkbox %}
|
{% if field|is_checkbox %}
|
||||||
<div class="checkbox"><label for="{{field.auto_id}}">{{field}}{{field.label}}</label></div>
|
<div class="checkbox"><label for="{{field.auto_id}}">{{field}}{{field.label}}</label></div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
29
cas_server/tests/auth.py
Normal file
29
cas_server/tests/auth.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License version 3
|
||||||
|
# along with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
#
|
||||||
|
# (c) 2016 Valentin Samir
|
||||||
|
"""Some test authentication classes for the CAS"""
|
||||||
|
from cas_server import auth
|
||||||
|
|
||||||
|
|
||||||
|
class TestCachedAttributesAuthUser(auth.TestAuthUser):
|
||||||
|
"""
|
||||||
|
A test authentication class only working for one unique user.
|
||||||
|
|
||||||
|
:param unicode username: A username, stored in the :attr:`username<AuthUser.username>`
|
||||||
|
class attribute. The uniq valid value is ``settings.CAS_TEST_USER``.
|
||||||
|
"""
|
||||||
|
def attributs(self):
|
||||||
|
"""
|
||||||
|
The user attributes.
|
||||||
|
|
||||||
|
:raises NotImplementedError: as this class do not support fetching user attributes
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
@ -185,6 +185,17 @@ class UserModels(object):
|
|||||||
).update(date=new_date)
|
).update(date=new_date)
|
||||||
return client
|
return client
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def tgt_expired_user(sec):
|
||||||
|
"""return a user logged since sec seconds"""
|
||||||
|
client = get_auth_client()
|
||||||
|
new_date = timezone.now() - timedelta(seconds=(sec))
|
||||||
|
models.User.objects.filter(
|
||||||
|
username=settings.CAS_TEST_USER,
|
||||||
|
session_key=client.session.session_key
|
||||||
|
).update(last_login=new_date)
|
||||||
|
return client
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user(client):
|
def get_user(client):
|
||||||
"""return the user associated with an authenticated client"""
|
"""return the user associated with an authenticated client"""
|
||||||
|
@ -114,6 +114,24 @@ class FederateSLOTestCase(TestCase, UserModels):
|
|||||||
models.FederateSLO.objects.get(username="test1@example.com")
|
models.FederateSLO.objects.get(username="test1@example.com")
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(CAS_AUTH_CLASS='cas_server.auth.TestAuthUser')
|
||||||
|
class UserAttributesTestCase(TestCase, UserModels):
|
||||||
|
"""test for the user attributes cache model"""
|
||||||
|
def test_clean_old_entries(self):
|
||||||
|
"""test the clean_old_entries methode"""
|
||||||
|
client = get_auth_client()
|
||||||
|
user = self.get_user(client)
|
||||||
|
models.UserAttributes.objects.create(username=settings.CAS_TEST_USER)
|
||||||
|
|
||||||
|
# test that attribute cache is removed for non existant users
|
||||||
|
self.assertEqual(len(models.UserAttributes.objects.all()), 1)
|
||||||
|
models.UserAttributes.clean_old_entries()
|
||||||
|
self.assertEqual(len(models.UserAttributes.objects.all()), 1)
|
||||||
|
user.delete()
|
||||||
|
models.UserAttributes.clean_old_entries()
|
||||||
|
self.assertEqual(len(models.UserAttributes.objects.all()), 0)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(CAS_AUTH_CLASS='cas_server.auth.TestAuthUser')
|
@override_settings(CAS_AUTH_CLASS='cas_server.auth.TestAuthUser')
|
||||||
class UserTestCase(TestCase, UserModels):
|
class UserTestCase(TestCase, UserModels):
|
||||||
"""tests for the user models"""
|
"""tests for the user models"""
|
||||||
@ -144,6 +162,24 @@ class UserTestCase(TestCase, UserModels):
|
|||||||
# assert the user has being well delete
|
# assert the user has being well delete
|
||||||
self.assertEqual(len(models.User.objects.all()), 0)
|
self.assertEqual(len(models.User.objects.all()), 0)
|
||||||
|
|
||||||
|
@override_settings(CAS_TGT_VALIDITY=3600)
|
||||||
|
def test_clean_old_entries_tgt_expired(self):
|
||||||
|
"""test clean_old_entiers with CAS_TGT_VALIDITY set"""
|
||||||
|
# get an authenticated client
|
||||||
|
client = self.tgt_expired_user(settings.CAS_TGT_VALIDITY + 60)
|
||||||
|
# assert the user exists before being cleaned
|
||||||
|
self.assertEqual(len(models.User.objects.all()), 1)
|
||||||
|
# assert the last lofin date is before the expiry date
|
||||||
|
self.assertTrue(
|
||||||
|
self.get_user(client).last_login < (
|
||||||
|
timezone.now() - timedelta(seconds=settings.CAS_TGT_VALIDITY)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# delete old inactive users
|
||||||
|
models.User.clean_old_entries()
|
||||||
|
# assert the user has being well delete
|
||||||
|
self.assertEqual(len(models.User.objects.all()), 0)
|
||||||
|
|
||||||
def test_clean_deleted_sessions(self):
|
def test_clean_deleted_sessions(self):
|
||||||
"""test clean_deleted_sessions"""
|
"""test clean_deleted_sessions"""
|
||||||
# get an authenticated client
|
# get an authenticated client
|
||||||
@ -177,6 +213,24 @@ class UserTestCase(TestCase, UserModels):
|
|||||||
self.assertFalse(models.ServiceTicket.objects.all())
|
self.assertFalse(models.ServiceTicket.objects.all())
|
||||||
self.assertTrue(client2.session.get("authenticated"))
|
self.assertTrue(client2.session.get("authenticated"))
|
||||||
|
|
||||||
|
@override_settings(CAS_AUTH_CLASS='cas_server.tests.auth.TestCachedAttributesAuthUser')
|
||||||
|
def test_cached_attributs(self):
|
||||||
|
"""
|
||||||
|
Test gettting user attributes from cache for auth method that do not support direct
|
||||||
|
fetch (link the ldap bind auth methode)
|
||||||
|
"""
|
||||||
|
client = get_auth_client()
|
||||||
|
user = self.get_user(client)
|
||||||
|
# if no cache is defined, the attributes are empty
|
||||||
|
self.assertEqual(user.attributs, {})
|
||||||
|
user_attr = models.UserAttributes.objects.create(username=settings.CAS_TEST_USER)
|
||||||
|
# if a cache is defined but without atrributes, also empty
|
||||||
|
self.assertEqual(user.attributs, {})
|
||||||
|
user_attr.attributs = settings.CAS_TEST_ATTRIBUTES
|
||||||
|
user_attr.save()
|
||||||
|
# attributes are what is found in the cache
|
||||||
|
self.assertEqual(user.attributs, settings.CAS_TEST_ATTRIBUTES)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(CAS_AUTH_CLASS='cas_server.auth.TestAuthUser')
|
@override_settings(CAS_AUTH_CLASS='cas_server.auth.TestAuthUser')
|
||||||
class TicketTestCase(TestCase, UserModels, BaseServicePattern):
|
class TicketTestCase(TestCase, UserModels, BaseServicePattern):
|
||||||
|
@ -132,6 +132,37 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin):
|
|||||||
# The LoginTicket is conssumed and should no longer be valid
|
# The LoginTicket is conssumed and should no longer be valid
|
||||||
self.assertTrue(params['lt'] not in client.session['lt'])
|
self.assertTrue(params['lt'] not in client.session['lt'])
|
||||||
|
|
||||||
|
def test_login_post_missing_params(self):
|
||||||
|
"""Test a login attempt with missing POST parameters (username or password or both)"""
|
||||||
|
# we get a client who fetch a frist time the login page and the login form default
|
||||||
|
# parameters
|
||||||
|
client, params = get_login_page_params()
|
||||||
|
# we set only set username
|
||||||
|
params["username"] = settings.CAS_TEST_USER
|
||||||
|
# we post a login attempt
|
||||||
|
response = client.post('/login', params)
|
||||||
|
# as the LT is not valid, login should fail
|
||||||
|
self.assert_login_failed(client, response)
|
||||||
|
|
||||||
|
# we get a client who fetch a frist time the login page and the login form default
|
||||||
|
# parameters
|
||||||
|
client, params = get_login_page_params()
|
||||||
|
# we set only set password
|
||||||
|
params["password"] = settings.CAS_TEST_PASSWORD
|
||||||
|
# we post a login attempt
|
||||||
|
response = client.post('/login', params)
|
||||||
|
# as the LT is not valid, login should fail
|
||||||
|
self.assert_login_failed(client, response)
|
||||||
|
|
||||||
|
# we get a client who fetch a frist time the login page and the login form default
|
||||||
|
# parameters
|
||||||
|
client, params = get_login_page_params()
|
||||||
|
# we set neither username nor password
|
||||||
|
# we post a login attempt
|
||||||
|
response = client.post('/login', params)
|
||||||
|
# as the LT is not valid, login should fail
|
||||||
|
self.assert_login_failed(client, response)
|
||||||
|
|
||||||
def test_login_view_post_goodpass_goodlt_warn(self):
|
def test_login_view_post_goodpass_goodlt_warn(self):
|
||||||
"""Test a successul login requesting to be warned before creating services tickets"""
|
"""Test a successul login requesting to be warned before creating services tickets"""
|
||||||
# get a client and initial login params
|
# get a client and initial login params
|
||||||
@ -794,7 +825,7 @@ class LogoutTestCase(TestCase):
|
|||||||
@override_settings(CAS_ENABLE_AJAX_AUTH=True)
|
@override_settings(CAS_ENABLE_AJAX_AUTH=True)
|
||||||
def test_ajax_logout(self):
|
def test_ajax_logout(self):
|
||||||
"""
|
"""
|
||||||
test ajax logout. These methode are here, but I do not really see an use case for
|
test ajax logout. These methods are here, but I do not really see an use case for
|
||||||
javascript logout
|
javascript logout
|
||||||
"""
|
"""
|
||||||
# get a client that is authenticated
|
# get a client that is authenticated
|
||||||
@ -1697,7 +1728,7 @@ class ProxyTestCase(TestCase, BaseServicePattern, XmlContent):
|
|||||||
self.assert_error(
|
self.assert_error(
|
||||||
response,
|
response,
|
||||||
"UNAUTHORIZED_SERVICE",
|
"UNAUTHORIZED_SERVICE",
|
||||||
'the service %s do not allow proxy ticket' % params['service']
|
'the service %s does not allow proxy tickets' % params['service']
|
||||||
)
|
)
|
||||||
|
|
||||||
self.service_pattern.proxy = True
|
self.service_pattern.proxy = True
|
||||||
@ -1943,7 +1974,7 @@ class SamlValidateTestCase(TestCase, BaseServicePattern, XmlContent):
|
|||||||
self.assert_error(
|
self.assert_error(
|
||||||
response,
|
response,
|
||||||
"AuthnFailed",
|
"AuthnFailed",
|
||||||
'TARGET %s do not match ticket service' % bad_target
|
'TARGET %s does not match ticket service' % bad_target
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_saml_bad_xml(self):
|
def test_saml_bad_xml(self):
|
||||||
|
@ -117,14 +117,18 @@ def import_attr(path):
|
|||||||
transform a python dotted path to the attr
|
transform a python dotted path to the attr
|
||||||
|
|
||||||
:param path: A dotted path to a python object or a python object
|
:param path: A dotted path to a python object or a python object
|
||||||
:type path: :obj:`unicode` or anything
|
:type path: :obj:`unicode` or :obj:`str` or anything
|
||||||
:return: The python object pointed by the dotted path or the python object unchanged
|
:return: The python object pointed by the dotted path or the python object unchanged
|
||||||
"""
|
"""
|
||||||
if not isinstance(path, str):
|
# if we got a str, decode it to unicode (normally it should only contain ascii)
|
||||||
|
if isinstance(path, six.binary_type):
|
||||||
|
path = path.decode("utf-8")
|
||||||
|
# if path is not an unicode, return it unchanged (may be it is already the attribute to import)
|
||||||
|
if not isinstance(path, six.text_type):
|
||||||
return path
|
return path
|
||||||
if "." not in path:
|
if u"." not in path:
|
||||||
ValueError("%r should be of the form `module.attr` and we just got `attr`" % path)
|
ValueError("%r should be of the form `module.attr` and we just got `attr`" % path)
|
||||||
module, attr = path.rsplit('.', 1)
|
module, attr = path.rsplit(u'.', 1)
|
||||||
try:
|
try:
|
||||||
return getattr(import_module(module), attr)
|
return getattr(import_module(module), attr)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -61,7 +61,7 @@ class LogoutMixin(object):
|
|||||||
username = self.request.session.get("username")
|
username = self.request.session.get("username")
|
||||||
if username:
|
if username:
|
||||||
if all_session:
|
if all_session:
|
||||||
logger.info("Logging out user %s from all of they sessions." % username)
|
logger.info("Logging out user %s from all sessions." % username)
|
||||||
else:
|
else:
|
||||||
logger.info("Logging out user %s." % username)
|
logger.info("Logging out user %s." % username)
|
||||||
users = []
|
users = []
|
||||||
@ -143,7 +143,7 @@ class LogoutView(View, LogoutMixin):
|
|||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
methode called on GET request on this view
|
method called on GET request on this view
|
||||||
|
|
||||||
:param django.http.HttpRequest request: The current request object
|
:param django.http.HttpRequest request: The current request object
|
||||||
"""
|
"""
|
||||||
@ -184,20 +184,20 @@ class LogoutView(View, LogoutMixin):
|
|||||||
logout_msg = _(
|
logout_msg = _(
|
||||||
"<h3>Logout successful</h3>"
|
"<h3>Logout successful</h3>"
|
||||||
"You have successfully logged out from the Central Authentication Service. "
|
"You have successfully logged out from the Central Authentication Service. "
|
||||||
"For security reasons, exit your web browser."
|
"For security reasons, close your web browser."
|
||||||
)
|
)
|
||||||
elif session_nb > 1:
|
elif session_nb > 1:
|
||||||
logout_msg = _(
|
logout_msg = _(
|
||||||
"<h3>Logout successful</h3>"
|
"<h3>Logout successful</h3>"
|
||||||
"You have successfully logged out from %s sessions of the Central "
|
"You have successfully logged out from %s sessions of the Central "
|
||||||
"Authentication Service. "
|
"Authentication Service. "
|
||||||
"For security reasons, exit your web browser."
|
"For security reasons, close your web browser."
|
||||||
) % session_nb
|
) % session_nb
|
||||||
else:
|
else:
|
||||||
logout_msg = _(
|
logout_msg = _(
|
||||||
"<h3>Logout successful</h3>"
|
"<h3>Logout successful</h3>"
|
||||||
"You were already logged out from the Central Authentication Service. "
|
"You were already logged out from the Central Authentication Service. "
|
||||||
"For security reasons, exit your web browser."
|
"For security reasons, close your web browser."
|
||||||
)
|
)
|
||||||
|
|
||||||
# depending of settings, redirect to the login page with a logout message or display
|
# depending of settings, redirect to the login page with a logout message or display
|
||||||
@ -229,7 +229,7 @@ class LogoutView(View, LogoutMixin):
|
|||||||
|
|
||||||
class FederateAuth(CsrfExemptView):
|
class FederateAuth(CsrfExemptView):
|
||||||
"""
|
"""
|
||||||
view to authenticated user agains a backend CAS then CAS_FEDERATE is True
|
view to authenticated user against a backend CAS then CAS_FEDERATE is True
|
||||||
|
|
||||||
csrf is disabled for allowing SLO requests reception.
|
csrf is disabled for allowing SLO requests reception.
|
||||||
"""
|
"""
|
||||||
@ -261,7 +261,7 @@ class FederateAuth(CsrfExemptView):
|
|||||||
"""
|
"""
|
||||||
# if settings.CAS_FEDERATE is not True redirect to the login page
|
# if settings.CAS_FEDERATE is not True redirect to the login page
|
||||||
if not settings.CAS_FEDERATE:
|
if not settings.CAS_FEDERATE:
|
||||||
logger.warning("CAS_FEDERATE is False, set it to True to use the federated mode")
|
logger.warning("CAS_FEDERATE is False, set it to True to use federation")
|
||||||
return redirect("cas_server:login")
|
return redirect("cas_server:login")
|
||||||
# POST with a provider suffix, this is probably an SLO request. csrf is disabled for
|
# POST with a provider suffix, this is probably an SLO request. csrf is disabled for
|
||||||
# allowing SLO requests reception
|
# allowing SLO requests reception
|
||||||
@ -305,13 +305,13 @@ class FederateAuth(CsrfExemptView):
|
|||||||
"""
|
"""
|
||||||
# if settings.CAS_FEDERATE is not True redirect to the login page
|
# if settings.CAS_FEDERATE is not True redirect to the login page
|
||||||
if not settings.CAS_FEDERATE:
|
if not settings.CAS_FEDERATE:
|
||||||
logger.warning("CAS_FEDERATE is False, set it to True to use the federated mode")
|
logger.warning("CAS_FEDERATE is False, set it to True to use federation")
|
||||||
return redirect("cas_server:login")
|
return redirect("cas_server:login")
|
||||||
renew = bool(request.GET.get('renew') and request.GET['renew'] != "False")
|
renew = bool(request.GET.get('renew') and request.GET['renew'] != "False")
|
||||||
# Is the user is already authenticated, no need to request authentication to the user
|
# Is the user is already authenticated, no need to request authentication to the user
|
||||||
# identity provider.
|
# identity provider.
|
||||||
if self.request.session.get("authenticated") and not renew:
|
if self.request.session.get("authenticated") and not renew:
|
||||||
logger.warning("User already authenticated, dropping federate authentication request")
|
logger.warning("User already authenticated, dropping federated authentication request")
|
||||||
return redirect("cas_server:login")
|
return redirect("cas_server:login")
|
||||||
try:
|
try:
|
||||||
# get the identity provider from its suffix
|
# get the identity provider from its suffix
|
||||||
@ -320,7 +320,7 @@ class FederateAuth(CsrfExemptView):
|
|||||||
auth = self.get_cas_client(request, provider, renew)
|
auth = self.get_cas_client(request, provider, renew)
|
||||||
# if no ticket submited, redirect to the identity provider CAS login page
|
# if no ticket submited, redirect to the identity provider CAS login page
|
||||||
if 'ticket' not in request.GET:
|
if 'ticket' not in request.GET:
|
||||||
logger.info("Trying to authenticate again %s" % auth.provider.server_url)
|
logger.info("Trying to authenticate %s again" % auth.provider.server_url)
|
||||||
return HttpResponseRedirect(auth.get_login_url())
|
return HttpResponseRedirect(auth.get_login_url())
|
||||||
else:
|
else:
|
||||||
ticket = request.GET['ticket']
|
ticket = request.GET['ticket']
|
||||||
@ -360,8 +360,8 @@ class FederateAuth(CsrfExemptView):
|
|||||||
else:
|
else:
|
||||||
logger.info(
|
logger.info(
|
||||||
(
|
(
|
||||||
"Got a invalid ticket %s from %s for service %s. "
|
"Got an invalid ticket %s from %s for service %s. "
|
||||||
"Retrying to authenticate"
|
"Retrying authentication"
|
||||||
) % (
|
) % (
|
||||||
ticket,
|
ticket,
|
||||||
auth.provider.server_url,
|
auth.provider.server_url,
|
||||||
@ -485,7 +485,7 @@ class LoginView(View, LogoutMixin):
|
|||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
methode called on POST request on this view
|
method called on POST request on this view
|
||||||
|
|
||||||
:param django.http.HttpRequest request: The current request object
|
:param django.http.HttpRequest request: The current request object
|
||||||
"""
|
"""
|
||||||
@ -497,7 +497,7 @@ class LoginView(View, LogoutMixin):
|
|||||||
messages.add_message(
|
messages.add_message(
|
||||||
self.request,
|
self.request,
|
||||||
messages.ERROR,
|
messages.ERROR,
|
||||||
_(u"Invalid login ticket, please retry to login")
|
_(u"Invalid login ticket, please try to log in again")
|
||||||
)
|
)
|
||||||
elif ret == self.USER_LOGIN_OK:
|
elif ret == self.USER_LOGIN_OK:
|
||||||
# On successful login, update the :class:`models.User<cas_server.models.User>` ``date``
|
# On successful login, update the :class:`models.User<cas_server.models.User>` ``date``
|
||||||
@ -506,6 +506,7 @@ class LoginView(View, LogoutMixin):
|
|||||||
username=self.request.session['username'],
|
username=self.request.session['username'],
|
||||||
session_key=self.request.session.session_key
|
session_key=self.request.session.session_key
|
||||||
)[0]
|
)[0]
|
||||||
|
self.user.last_login = timezone.now()
|
||||||
self.user.save()
|
self.user.save()
|
||||||
elif ret == self.USER_LOGIN_FAILURE: # bad user login
|
elif ret == self.USER_LOGIN_FAILURE: # bad user login
|
||||||
if settings.CAS_FEDERATE:
|
if settings.CAS_FEDERATE:
|
||||||
@ -554,7 +555,7 @@ class LoginView(View, LogoutMixin):
|
|||||||
"""
|
"""
|
||||||
if not self.check_lt():
|
if not self.check_lt():
|
||||||
self.init_form(self.request.POST)
|
self.init_form(self.request.POST)
|
||||||
logger.warning("Receive an invalid login ticket")
|
logger.warning("Received an invalid login ticket")
|
||||||
return self.INVALID_LOGIN_TICKET
|
return self.INVALID_LOGIN_TICKET
|
||||||
elif not self.request.session.get("authenticated") or self.renew:
|
elif not self.request.session.get("authenticated") or self.renew:
|
||||||
# authentication request receive, initialize the form to use
|
# authentication request receive, initialize the form to use
|
||||||
@ -569,10 +570,10 @@ class LoginView(View, LogoutMixin):
|
|||||||
logger.info("User %s successfully authenticated" % self.request.session["username"])
|
logger.info("User %s successfully authenticated" % self.request.session["username"])
|
||||||
return self.USER_LOGIN_OK
|
return self.USER_LOGIN_OK
|
||||||
else:
|
else:
|
||||||
logger.warning("A logging attemps failed")
|
logger.warning("A login attempt failed")
|
||||||
return self.USER_LOGIN_FAILURE
|
return self.USER_LOGIN_FAILURE
|
||||||
else:
|
else:
|
||||||
logger.warning("Receuve a logging attempt whereas the user is already logged")
|
logger.warning("Received a login attempt for an already-active user")
|
||||||
return self.USER_ALREADY_LOGGED
|
return self.USER_ALREADY_LOGGED
|
||||||
|
|
||||||
def init_get(self, request):
|
def init_get(self, request):
|
||||||
@ -600,7 +601,7 @@ class LoginView(View, LogoutMixin):
|
|||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
methode called on GET request on this view
|
method called on GET request on this view
|
||||||
|
|
||||||
:param django.http.HttpRequest request: The current request object
|
:param django.http.HttpRequest request: The current request object
|
||||||
"""
|
"""
|
||||||
@ -667,7 +668,7 @@ class LoginView(View, LogoutMixin):
|
|||||||
|
|
||||||
def service_login(self):
|
def service_login(self):
|
||||||
"""
|
"""
|
||||||
Perform login agains a service
|
Perform login against a service
|
||||||
|
|
||||||
:return:
|
:return:
|
||||||
* The rendering of the ``settings.CAS_WARN_TEMPLATE`` if the user asked to be
|
* The rendering of the ``settings.CAS_WARN_TEMPLATE`` if the user asked to be
|
||||||
@ -801,7 +802,7 @@ class LoginView(View, LogoutMixin):
|
|||||||
else:
|
else:
|
||||||
return utils.redirect_params("cas_server:login", params=self.request.GET)
|
return utils.redirect_params("cas_server:login", params=self.request.GET)
|
||||||
|
|
||||||
# if login agains a service
|
# if login against a service
|
||||||
if self.service:
|
if self.service:
|
||||||
return self.service_login()
|
return self.service_login()
|
||||||
# else display the logged template
|
# else display the logged template
|
||||||
@ -949,7 +950,7 @@ class Auth(CsrfExemptView):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def post(request):
|
def post(request):
|
||||||
"""
|
"""
|
||||||
methode called on POST request on this view
|
method called on POST request on this view
|
||||||
|
|
||||||
:param django.http.HttpRequest request: The current request object
|
:param django.http.HttpRequest request: The current request object
|
||||||
:return: ``HttpResponse(u"yes\\n")`` if the POSTed tuple (username, password, service)
|
:return: ``HttpResponse(u"yes\\n")`` if the POSTed tuple (username, password, service)
|
||||||
@ -1005,7 +1006,7 @@ class Validate(View):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get(request):
|
def get(request):
|
||||||
"""
|
"""
|
||||||
methode called on GET request on this view
|
method called on GET request on this view
|
||||||
|
|
||||||
:param django.http.HttpRequest request: The current request object
|
:param django.http.HttpRequest request: The current request object
|
||||||
:return:
|
:return:
|
||||||
@ -1116,7 +1117,7 @@ class ValidateService(View):
|
|||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
"""
|
"""
|
||||||
methode called on GET request on this view
|
method called on GET request on this view
|
||||||
|
|
||||||
:param django.http.HttpRequest request: The current request object:
|
:param django.http.HttpRequest request: The current request object:
|
||||||
:return: The rendering of ``cas_server/serviceValidate.xml`` if no errors is raised,
|
:return: The rendering of ``cas_server/serviceValidate.xml`` if no errors is raised,
|
||||||
@ -1284,7 +1285,7 @@ class Proxy(View):
|
|||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
"""
|
"""
|
||||||
methode called on GET request on this view
|
method called on GET request on this view
|
||||||
|
|
||||||
:param django.http.HttpRequest request: The current request object:
|
:param django.http.HttpRequest request: The current request object:
|
||||||
:return: The returned value of :meth:`process_proxy` if no error is raised,
|
:return: The returned value of :meth:`process_proxy` if no error is raised,
|
||||||
@ -1323,7 +1324,7 @@ class Proxy(View):
|
|||||||
if not pattern.proxy:
|
if not pattern.proxy:
|
||||||
raise ValidateError(
|
raise ValidateError(
|
||||||
u'UNAUTHORIZED_SERVICE',
|
u'UNAUTHORIZED_SERVICE',
|
||||||
u'the service %s do not allow proxy ticket' % self.target_service
|
u'the service %s does not allow proxy tickets' % self.target_service
|
||||||
)
|
)
|
||||||
# is the proxy granting ticket valid
|
# is the proxy granting ticket valid
|
||||||
ticket = ProxyGrantingTicket.get(self.pgt)
|
ticket = ProxyGrantingTicket.get(self.pgt)
|
||||||
@ -1387,7 +1388,7 @@ class SamlValidate(CsrfExemptView):
|
|||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""
|
"""
|
||||||
methode called on POST request on this view
|
method called on POST request on this view
|
||||||
|
|
||||||
:param django.http.HttpRequest request: The current request object
|
:param django.http.HttpRequest request: The current request object
|
||||||
:return: the rendering of ``cas_server/samlValidate.xml`` if no error is raised,
|
:return: the rendering of ``cas_server/samlValidate.xml`` if no error is raised,
|
||||||
@ -1417,7 +1418,7 @@ class SamlValidate(CsrfExemptView):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"SamlValidate: User attributs are:\n%s" % pprint.pformat(self.ticket.attributs)
|
"SamlValidate: User attributes are:\n%s" % pprint.pformat(self.ticket.attributs)
|
||||||
)
|
)
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
@ -1446,7 +1447,7 @@ class SamlValidate(CsrfExemptView):
|
|||||||
if ticket.service != self.target:
|
if ticket.service != self.target:
|
||||||
raise SamlValidateError(
|
raise SamlValidateError(
|
||||||
u'AuthnFailed',
|
u'AuthnFailed',
|
||||||
u'TARGET %s do not match ticket service' % self.target
|
u'TARGET %s does not match ticket service' % self.target
|
||||||
)
|
)
|
||||||
return ticket
|
return ticket
|
||||||
except (IndexError, KeyError):
|
except (IndexError, KeyError):
|
||||||
|
Loading…
Reference in New Issue
Block a user