Commit ba590607 authored by Valentin Samir's avatar Valentin Samir Committed by GitHub

Merge pull request #9 from nitmir/dev

Update to version 0.6.2
parents 8b27b8c5 773707e6
language: python
matrix:
include:
- python: "2.7"
env: TOX_ENV=coverage
- python: "2.7"
env: TOX_ENV=flake8
- python: "2.7"
......@@ -23,6 +21,8 @@ matrix:
env: TOX_ENV=py35-django18
- python: "3.5"
env: TOX_ENV=py35-django19
- python: "2.7"
env: TOX_ENV=coverage
cache:
directories:
- $HOME/.cache/pip/http/
......
......@@ -38,7 +38,7 @@ dist:
test_venv/bin/python:
virtualenv test_venv
test_venv/bin/pip install -U --requirement requirements-dev.txt Django
test_venv/bin/pip install -U --requirement requirements-dev.txt 'Django<1.10'
test_venv/cas/manage.py: test_venv
mkdir -p test_venv/cas
......@@ -62,7 +62,7 @@ run_server: test_project
run_tests: test_venv
python setup.py check --restructuredtext --stric
test_venv/bin/py.test --cov=cas_server --cov-report html
test_venv/bin/py.test -rw -x --cov=cas_server --cov-report html
rm htmlcov/coverage_html.js # I am really pissed off by those keybord shortcuts
test_venv/bin/sphinx-build: test_venv
......
......@@ -29,11 +29,49 @@ Dependencies
``django-cas-server`` depends on the following python packages:
* Django >= 1.7 < 1.10
* Django >= 1.7.1 < 1.10
* requests >= 2.4
* requests_futures >= 0.9.5
* lxml >= 3.4
* six >= 1
* six >= 1.8
Minimal version of packages dependancy are just indicative and meens that ``django-cas-server`` has
been tested with it. Previous versions of dependencies may or may not work.
Additionally, denpending of the authentication backend you plan to use, you may need the following
python packages:
* ldap3
* psycopg2
* mysql-python
Here there is a table with the name of python packages and the corresponding packages providing
them on debian like systems and centos like systems.
You should try as much as possible to use system packages as there are automatically updated then
you update your system. You can then install Not Available (N/A)
packages on your system using pip inside a virtualenv as described in the `Installation`_ section.
For use with python3, just replace python(2) in the table by python3.
+------------------+-------------------------+---------------------+
| python package | debian like systems | centos like systems |
+==================+=========================+=====================+
| Django | python-django | python-django |
+------------------+-------------------------+---------------------+
| requests | python-requests | python-requests |
+------------------+-------------------------+---------------------+
| requests_futures | python-requests-futures | N/A |
+------------------+-------------------------+---------------------+
| lxml | python-lxml | python-lxml |
+------------------+-------------------------+---------------------+
| six | python-six | python-six |
+------------------+-------------------------+---------------------+
| ldap3 | python-ldap3 | python-ldap3 |
+------------------+-------------------------+---------------------+
| psycopg2 | python-psycopg2 | python-psycopg2 |
+------------------+-------------------------+---------------------+
| mysql-python | python-mysqldb | python2-mysql |
+------------------+-------------------------+---------------------+
Installation
============
......@@ -63,14 +101,17 @@ The recommended installation mode is to use a virtualenv with ``--system-site-pa
New python executable in cas/bin/python2
Also creating executable in cas/bin/python
Installing setuptools, pip...done.
4. And `activate it <https://virtualenv.pypa.io/en/stable/userguide/#activate-script>`__::
$ cd cas_venv/; . bin/activate
4. Create a django project::
5. Create a django project::
$ django-admin startproject cas_project
$ cd cas_project
5. Install `django-cas-server`. To use the last published release, run::
6. Install `django-cas-server`. To use the last published release, run::
$ pip install django-cas-server
......@@ -81,11 +122,11 @@ The recommended installation mode is to use a virtualenv with ``--system-site-pa
$ pip install -r requirements.txt
Then, either run ``make install`` to create a python package using the sources of the repository
and install it with pip, or place the `cas_server` directory into your
and install it with pip, or place the ``cas_server`` directory into your
`PYTHONPATH <https://docs.python.org/2/using/cmdline.html#envvar-PYTHONPATH>`_
(for instance by symlinking `cas_server` to the root of your django project).
(for instance by symlinking ``cas_server`` to the root of your django project).
6. Open ``cas_project/settings.py`` in you favourite editor and follow the quick start section.
7. Open ``cas_project/settings.py`` in you favourite editor and follow the quick start section.
Quick start
......@@ -145,7 +186,7 @@ Quick start
6. Start the development server and visit http://127.0.0.1:8000/admin/
to add a first service allowed to authenticate user against the CAS
(you'll need the Admin app enabled). See the Service Patterns section bellow.
(you'll need the Admin app enabled). See the `Service Patterns`_ section bellow.
7. Visit http://127.0.0.1:8000/cas/ to login with your django users.
......@@ -163,6 +204,8 @@ Template settings
* ``CAS_LOGO_URL``: URL to the logo showed in the up left corner on the default
templates. Set it to ``False`` to disable it.
* ``CAS_FAVICON_URL``: URL to the favicon (shortcut icon) used by the default templates.
Default is a key icon. Set it to ``False`` to disable it.
* ``CAS_COMPONENT_URLS``: URLs to css and javascript external components. It is a dictionnary
and it must have the five following keys: ``"bootstrap3_css"``, ``"bootstrap3_js"``,
``"html5shiv"``, ``"respond"``, ``"jquery"``. The default is::
......@@ -191,12 +234,14 @@ Template settings
Authentication settings
-----------------------
* ``CAS_AUTH_CLASS``: A dotted path to a class or a class implementing
``cas_server.auth.AuthUser``. The default is ``"cas_server.auth.DjangoAuthUser"``
* ``CAS_AUTH_CLASS``: A dotted path to a class or a class implementing
``cas_server.auth.AuthUser``. The default is ``"cas_server.auth.DjangoAuthUser"``
Available classes bundled with ``django-cas-server`` are listed below in the
`Authentication backend`_ section.
* ``SESSION_COOKIE_AGE``: This is a django settings. Here, it control the delay in seconds after
which inactive users are logged out. The default is ``1209600`` (2 weeks). You probably should
reduce it to something like ``86400`` seconds (1 day).
* ``SESSION_COOKIE_AGE``: This is a django settings. Here, it control the delay in seconds after
which inactive users are logged out. The default is ``1209600`` (2 weeks). You probably should
reduce it to something like ``86400`` seconds (1 day).
* ``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
......@@ -212,13 +257,23 @@ Authentication settings
Federation settings
-------------------
* ``CAS_FEDERATE``: A boolean for activating the federated mode (see the federate section below).
The default is ``False``.
* ``CAS_FEDERATE``: A boolean for activating the federated mode (see the `Federation mode`_
section below). The default is ``False``.
* ``CAS_FEDERATE_REMEMBER_TIMEOUT``: Time after witch the cookie use for "remember my identity
provider" expire. The default is ``604800``, one week. The cookie is called
``_remember_provider``.
New version warnings settings
-----------------------------
* ``CAS_NEW_VERSION_HTML_WARNING``: A boolean for diplaying a warning on html pages then a new
version of the application is avaible. Once closed by a user, it is not displayed to this user
until the next new version. The default is ``True``.
* ``CAS_NEW_VERSION_EMAIL_WARNING``: A bolean sot sending a email to ``settings.ADMINS`` when a new
version is available. The default is ``True``.
Tickets validity settings
-------------------------
......@@ -257,6 +312,7 @@ Tickets miscellaneous settings
Mysql backend settings
----------------------
Deprecated, see the `Sql backend settings`_.
Only usefull if you are using the mysql authentication backend:
* ``CAS_SQL_HOST``: Host for the SQL server. The default is ``"localhost"``.
......@@ -283,6 +339,64 @@ Only usefull if you are using the mysql authentication backend:
The default is ``"crypt"``.
Sql backend settings
--------------------
Only usefull if you are using the sql authentication backend. You must add a ``"cas_server"``
database to `settings.DATABASES <https://docs.djangoproject.com/fr/1.9/ref/settings/#std:setting-DATABASES>`__
as defined in the django documentation. It is then the database
use by the sql backend.
* ``CAS_SQL_USER_QUERY``: The query performed upon user authentication.
The username must be in field ``username``, the password in ``password``,
additional fields are used as the user attributes.
The default is ``"SELECT user AS username, pass AS password, users.* FROM users WHERE user = %s"``
* ``CAS_SQL_PASSWORD_CHECK``: The method used to check the user password. Must be one of the following:
* ``"crypt"`` (see <https://en.wikipedia.org/wiki/Crypt_(C)>), the password in the database
should begin this $
* ``"ldap"`` (see https://tools.ietf.org/id/draft-stroeder-hashed-userpassword-values-01.html)
the password in the database must begin with one of {MD5}, {SMD5}, {SHA}, {SSHA}, {SHA256},
{SSHA256}, {SHA384}, {SSHA384}, {SHA512}, {SSHA512}, {CRYPT}.
* ``"hex_HASH_NAME"`` with ``HASH_NAME`` in md5, sha1, sha224, sha256, sha384, sha512.
The hashed password in the database is compare to the hexadecimal digest of the clear
password hashed with the corresponding algorithm.
* ``"plain"``, the password in the database must be in clear.
The default is ``"crypt"``.
* ``CAS_SQL_PASSWORD_CHARSET``: Charset the SQL users passwords was hash with. This is needed to
encode the user sended password before hashing it for comparison. The default is ``"utf-8"``.
Ldap backend settings
---------------------
Only usefull if you are using the ldap authentication backend:
* ``CAS_LDAP_SERVER``: Address of the LDAP server. The default is ``"localhost"``.
* ``CAS_LDAP_USER``: User bind address, for example ``"cn=admin,dc=crans,dc=org"`` for
connecting to the LDAP server.
* ``CAS_LDAP_PASSWORD``: Password for connecting to the LDAP server.
* ``CAS_LDAP_BASE_DN``: LDAP search base DN, for example ``"ou=data,dc=crans,dc=org"``.
* ``CAS_LDAP_USER_QUERY``: Search filter for searching user by username. User inputed usernames are
escaped using ``ldap3.utils.conv.escape_bytes``. The default is ``"(uid=%s)"``
* ``CAS_LDAP_USERNAME_ATTR``: Attribute used for users usernames. The default is ``"uid"``
* ``CAS_LDAP_PASSWORD_ATTR``: Attribute used for users passwords. The default is ``"userPassword"``
* ``CAS_LDAP_PASSWORD_CHECK``: The method used to check the user password. Must be one of the following:
* ``"crypt"`` (see <https://en.wikipedia.org/wiki/Crypt_(C)>), the password in the database
should begin this $
* ``"ldap"`` (see https://tools.ietf.org/id/draft-stroeder-hashed-userpassword-values-01.html)
the password in the database must begin with one of {MD5}, {SMD5}, {SHA}, {SSHA}, {SHA256},
{SSHA256}, {SHA384}, {SSHA384}, {SHA512}, {SSHA512}, {CRYPT}.
* ``"hex_HASH_NAME"`` with ``HASH_NAME`` in md5, sha1, sha224, sha256, sha384, sha512.
The hashed password in the database is compare to the hexadecimal digest of the clear
password hashed with the corresponding algorithm.
* ``"plain"``, the password in the database must be in clear.
The default is ``"ldap"``.
* ``CAS_LDAP_PASSWORD_CHARSET``: Charset the LDAP users passwords was hash with. This is needed to
encode the user sended password before hashing it for comparison. The default is ``"utf-8"``.
Test backend settings
---------------------
Only usefull if you are using the test authentication backend:
......@@ -304,11 +418,17 @@ Authentication backend
for the user are defined by the ``CAS_TEST_*`` settings.
* django backend ``cas_server.auth.DjangoAuthUser``: Users are authenticated against django users system.
This is the default backend. The returned attributes are the fields available on the user model.
* mysql backend ``cas_server.auth.MysqlAuthUser``: see the 'Mysql backend settings' section.
* mysql backend ``cas_server.auth.MysqlAuthUser``: Deprecated, use the sql backend instead.
see the `Mysql backend settings`_ section. The returned attributes are those return by sql query
``CAS_SQL_USER_QUERY``.
* sql backend ``cas_server.auth.SqlAuthUser``: see the `Sql backend settings`_ section.
The returned attributes are those return by sql query ``CAS_SQL_USER_QUERY``.
* ldap backend ``cas_server.auth.LdapAuthUser``: see the `Ldap backend settings`_ section.
The returned attributes are those of the ldap node returned by the query filter ``CAS_LDAP_USER_QUERY``.
* federated backend ``cas_server.auth.CASFederateAuth``: It is automatically used then ``CAS_FEDERATE`` is ``True``.
You should not set it manually without setting ``CAS_FEDERATE`` to ``True``.
Logs
====
......
......@@ -9,5 +9,9 @@
#
# (c) 2015-2016 Valentin Samir
"""A django CAS server application"""
#: version of the application
VERSION = '0.6.2'
#: path the the application configuration class
default_app_config = 'cas_server.apps.CasAppConfig'
......@@ -13,16 +13,25 @@
from django.conf import settings
from django.contrib.auth import get_user_model
from django.utils import timezone
from django.db import connections, DatabaseError
import warnings
from datetime import timedelta
from six.moves import range
try: # pragma: no cover
import MySQLdb
import MySQLdb.cursors
from utils import check_password
except ImportError:
MySQLdb = None
try: # pragma: no cover
import ldap3
except ImportError:
ldap3 = None
from .models import FederatedUser
from .utils import check_password, dictfetchall
class AuthUser(object):
......@@ -116,19 +125,46 @@ class TestAuthUser(AuthUser):
return {}
class MysqlAuthUser(AuthUser): # pragma: no cover
class DBAuthUser(AuthUser): # pragma: no cover
"""base class for databate based auth classes"""
#: DB user attributes as a :class:`dict` if the username is found in the database.
user = None
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
"""
if self.user:
return self.user
else:
return {}
class MysqlAuthUser(DBAuthUser): # pragma: no cover
"""
A mysql authentication class: authentication user agains a mysql database
DEPRECATED, use :class:`SqlAuthUser` instead.
A mysql authentication class: authenticate user agains a mysql database
:param unicode username: A username, stored in the :attr:`username<AuthUser.username>`
class attribute. Valid value are fetched from the MySQL database set with
``settings.CAS_SQL_*`` settings parameters using the query
``settings.CAS_SQL_USER_QUERY``.
"""
#: Mysql user attributes as a :class:`dict` if the username is found in the database.
user = None
def __init__(self, username):
warnings.warn(
(
"MysqlAuthUser authentication class is deprecated: "
"use cas_server.auth.SqlAuthUser instead"
),
UserWarning
)
# see the connect function at
# http://mysql-python.sourceforge.net/MySQLdb.html#functions-and-attributes
# for possible mysql config parameters.
......@@ -169,24 +205,130 @@ class MysqlAuthUser(AuthUser): # pragma: no cover
else:
return False
def attributs(self):
class SqlAuthUser(DBAuthUser): # pragma: no cover
"""
A SQL authentication class: authenticate user agains a SQL database. The SQL database
must be configures in settings.py as ``settings.DATABASES['cas_server']``.
:param unicode username: A username, stored in the :attr:`username<AuthUser.username>`
class attribute. Valid value are fetched from the MySQL database set with
``settings.CAS_SQL_*`` settings parameters using the query
``settings.CAS_SQL_USER_QUERY``.
"""
def __init__(self, username):
if "cas_server" not in connections:
raise RuntimeError("Please configure the 'cas_server' database in settings.DATABASES")
for retry_nb in range(3):
try:
with connections["cas_server"].cursor() as curs:
curs.execute(settings.CAS_SQL_USER_QUERY, (username,))
results = dictfetchall(curs)
if len(results) == 1:
self.user = results[0]
super(SqlAuthUser, self).__init__(self.user['username'])
else:
super(SqlAuthUser, self).__init__(username)
break
except DatabaseError:
connections["cas_server"].close()
if retry_nb == 2:
raise
def test_password(self, password):
"""
The user attributes.
Tests ``password`` agains the user password.
: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
:param unicode password: a clear text password as submited by the user.
:return: ``True`` if :attr:`username<AuthUser.username>` is valid and ``password`` is
correct, ``False`` otherwise.
:rtype: bool
"""
if self.user:
return self.user
return check_password(
settings.CAS_SQL_PASSWORD_CHECK,
password,
self.user["password"],
settings.CAS_SQL_PASSWORD_CHARSET
)
else:
return {}
return False
class LdapAuthUser(DBAuthUser): # pragma: no cover
"""
A ldap authentication class: authenticate user against a ldap database
:param unicode username: A username, stored in the :attr:`username<AuthUser.username>`
class attribute. Valid value are fetched from the ldap database set with
``settings.CAS_LDAP_*`` settings parameters.
"""
_conn = None
@classmethod
def get_conn(cls):
"""Return a connection object to the ldap database"""
conn = cls._conn
if conn is None or conn.closed:
conn = ldap3.Connection(
settings.CAS_LDAP_SERVER,
settings.CAS_LDAP_USER,
settings.CAS_LDAP_PASSWORD,
auto_bind=True
)
cls._conn = conn
return conn
def __init__(self, username):
if not ldap3:
raise RuntimeError("Please install ldap3 before using the LdapAuthUser backend")
# in case we got deconnected from the database, retry to connect 2 times
for retry_nb in range(3):
try:
conn = self.get_conn()
if conn.search(
settings.CAS_LDAP_BASE_DN,
settings.CAS_LDAP_USER_QUERY % ldap3.utils.conv.escape_bytes(username),
attributes=ldap3.ALL_ATTRIBUTES
) and len(conn.entries) == 1:
user = conn.entries[0].entry_get_attributes_dict()
if user.get(settings.CAS_LDAP_USERNAME_ATTR):
self.user = user
super(LdapAuthUser, self).__init__(user[settings.CAS_LDAP_USERNAME_ATTR][0])
else:
super(LdapAuthUser, self).__init__(username)
else:
super(LdapAuthUser, self).__init__(username)
break
except ldap3.LDAPCommunicationError:
if retry_nb == 2:
raise
def test_password(self, password):
"""
Tests ``password`` agains the user password.
:param unicode password: a clear text password as submited by the user.
:return: ``True`` if :attr:`username<AuthUser.username>` is valid and ``password`` is
correct, ``False`` otherwise.
:rtype: bool
"""
if self.user and self.user.get(settings.CAS_LDAP_PASSWORD_ATTR):
return check_password(
settings.CAS_LDAP_PASSWORD_CHECK,
password,
self.user[settings.CAS_LDAP_PASSWORD_ATTR][0],
settings.CAS_LDAP_PASSWORD_CHARSET
)
else:
return False
class DjangoAuthUser(AuthUser): # pragma: no cover
"""
A django auth class: authenticate user agains django internal users
A django auth class: authenticate user against django internal users
:param unicode username: A username, stored in the :attr:`username<AuthUser.username>`
class attribute. Valid value are usernames of django internal users.
......
......@@ -134,6 +134,14 @@ class CASClientBase(object):
raise CASError(errors[0].attrib['code'], errors[0].text)
raise CASError("Bad http code %s" % response.code)
@staticmethod
def get_page_charset(page, default="utf-8"):
content_type = page.info().get('Content-type')
if content_type and "charset=" in content_type:
return content_type.split("charset=")[-1]
else:
return default
class CASClientV1(CASClientBase, ReturnUnicode):
"""CAS Client Version 1"""
......@@ -146,17 +154,15 @@ class CASClientV1(CASClientBase, ReturnUnicode):
Returns username on success and None on failure.
"""
params = [('ticket', ticket), ('service', self.service_url)]
if self.renew:
params.append(('renew', 'true'))
url = (urllib_parse.urljoin(self.server_url, 'validate') + '?' +
urllib_parse.urlencode(params))
page = urllib_request.urlopen(url)
try:
verified = page.readline().strip()
if verified == b'yes':
content_type = page.info().get('Content-type')
if "charset=" in content_type:
charset = content_type.split("charset=")[-1]
else:
charset = "ascii"
charset = self.get_page_charset(page, default="ascii")
user = self.u(page.readline().strip(), charset)
return user, None, None
else:
......@@ -183,17 +189,15 @@ class CASClientV2(CASClientBase, ReturnUnicode):
def get_verification_response(self, ticket):
params = [('ticket', ticket), ('service', self.service_url)]
if self.renew:
params.append(('renew', 'true'))
if self.proxy_callback:
params.append(('pgtUrl', self.proxy_callback))
base_url = urllib_parse.urljoin(self.server_url, self.url_suffix)
url = base_url + '?' + urllib_parse.urlencode(params)
page = urllib_request.urlopen(url)
try:
content_type = page.info().get('Content-type')
if "charset=" in content_type:
charset = content_type.split("charset=")[-1]
else:
charset = "ascii"
charset = self.get_page_charset(page)
return (page.read(), charset)
finally:
page.close()
......@@ -306,11 +310,7 @@ class CASClientWithSAMLV1(CASClientV2, SingleLogoutMixin):
from elementtree import ElementTree
page = self.fetch_saml_validation(ticket)
content_type = page.info().get('Content-type')
if "charset=" in content_type:
charset = content_type.split("charset=")[-1]
else:
charset = "ascii"
charset = self.get_page_charset(page)
try:
user = None
......
......@@ -18,6 +18,8 @@ from importlib import import_module
#: URL to the logo showed in the up left corner on the default templates.
CAS_LOGO_URL = static("cas_server/logo.png")
#: URL to the favicon (shortcut icon) used by the default templates. Default is a key icon.
CAS_FAVICON_URL = static("cas_server/favicon.ico")
#: URLs to css and javascript external components.
CAS_COMPONENT_URLS = {
"bootstrap3_css": "//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css",
......@@ -110,12 +112,39 @@ CAS_SQL_PASSWORD = ''
CAS_SQL_DBNAME = ''
#: Database charset.
CAS_SQL_DBCHARSET = 'utf8'
#: The query performed upon user authentication.
CAS_SQL_USER_QUERY = 'SELECT user AS usersame, pass AS password, users.* FROM users WHERE user = %s'
CAS_SQL_USER_QUERY = 'SELECT user AS username, pass AS password, users.* FROM users WHERE user = %s'
#: The method used to check the user password. Must be one of ``"crypt"``, ``"ldap"``,
#: ``"hex_md5"``, ``"hex_sha1"``, ``"hex_sha224"``, ``"hex_sha256"``, ``"hex_sha384"``,
#: ``"hex_sha512"``, ``"plain"``.
CAS_SQL_PASSWORD_CHECK = 'crypt' # crypt or plain
CAS_SQL_PASSWORD_CHECK = 'crypt'
#: charset the SQL users passwords was hash with
CAS_SQL_PASSWORD_CHARSET = "utf-8"
#: Address of the LDAP server
CAS_LDAP_SERVER = 'localhost'
#: LDAP user bind address, for example ``"cn=admin,dc=crans,dc=org"`` for connecting to the LDAP
#: server.
CAS_LDAP_USER = None
#: LDAP connection password
CAS_LDAP_PASSWORD = None
#: LDAP seach base DN, for example ``"ou=data,dc=crans,dc=org"``.
CAS_LDAP_BASE_DN = None
#: LDAP search filter for searching user by username. User inputed usernames are escaped using
#: :func:`ldap3.utils.conv.escape_bytes`.
CAS_LDAP_USER_QUERY = "(uid=%s)"
#: LDAP attribute used for users usernames
CAS_LDAP_USERNAME_ATTR = "uid"
#: LDAP attribute used for users passwords
CAS_LDAP_PASSWORD_ATTR = "userPassword"
#: The method used to check the user password. Must be one of ``"crypt"``, ``"ldap"``,
#: ``"hex_md5"``, ``"hex_sha1"``, ``"hex_sha224"``, ``"hex_sha256"``, ``"hex_sha384"``,
#: ``"hex_sha512"``, ``"plain"``.
CAS_LDAP_PASSWORD_CHECK = "ldap"
#: charset the LDAP users passwords was hash with
CAS_LDAP_PASSWORD_CHARSET = "utf-8"
#: Username of the test user.
......@@ -140,6 +169,15 @@ CAS_FEDERATE = False
#: Time after witch the cookie use for “remember my identity provider” expire (one week).
CAS_FEDERATE_REMEMBER_TIMEOUT = 604800
#: A :class:`bool` for diplaying a warning on html pages then a new version of the application
#: is avaible. Once closed by a user, it is not displayed to this user until the next new version.
CAS_NEW_VERSION_HTML_WARNING = True
#: A :class:`bool` for sending emails to ``settings.ADMINS`` when a new version is available.
CAS_NEW_VERSION_EMAIL_WARNING = True
#: URL to the pypi json of the application. Used to retreive the version number of the last version.
#: You should not change it.
CAS_NEW_VERSION_JSON_URL = "https://pypi.python.org/pypi/django-cas-server/json"
GLOBALS = globals().copy()
for name, default_value in GLOBALS.items():
# get the current setting value, falling back to default_value
......
......@@ -42,13 +42,13 @@ class CASFederateValidateUser(object):
#: the identity provider
provider = None
def __init__(self, provider, service_url):
def __init__(self, provider, service_url, renew=False):
self.provider = provider
self.client = CASClient(
service_url=service_url,
version=provider.cas_protocol_version,
server_url=provider.server_url,
renew=False,
renew=renew,
)
def get_login_url(self):
......
......@@ -19,49 +19,56 @@ import cas_server.models as models
class BootsrapForm(forms.Form):
"""Form base class to use boostrap then rendering the form fields"""
"""
Bases: :class:`django.forms.Form`
Form base class to use boostrap then rendering the form fields
"""
def __init__(self, *args, **kwargs):
super(BootsrapForm, self).__init__(*args, **kwargs)
for (name, field) in self.fields.items():
for field in self.fields.values():
# Only tweak the fiel if it will be displayed
if not isinstance(field.widget, forms.HiddenInput):
# tell to display the field (used in form.html)
self[name].display = True
attrs = {}
if isinstance(field.widget, forms.CheckboxInput):
self[name].checkbox = True
else:
if not isinstance(field.widget, forms.CheckboxInput):
attrs['class'] = "form-control"
if field.label:
if field.label: # pragma: no branch (currently all field are hidden or labeled)
attrs["placeholder"] = field.label
if field.required:
attrs["required"] = "required"
field.widget.attrs.update(attrs)
class WarnForm(BootsrapForm):
class BaseLogin(BootsrapForm):
"""
Bases: :class:`django.forms.Form`
Bases: :class:`BootsrapForm`
Form used on warn page before emiting a ticket
Base form with all field possibly hidden on the login pages
"""
#: The service url for which the user want a ticket
service = forms.CharField(widget=forms.HiddenInput(), required=False)
#: A valid LoginTicket to prevent POST replay
lt = forms.CharField(widget=forms.HiddenInput(), required=False)
#: Is the service asking the authentication renewal ?
renew = forms.BooleanField(widget=forms.HiddenInput(), required=False)
#: Url to redirect to if the authentication fail (user not authenticated or bad service)
gateway = forms.CharField(widget=forms.HiddenInput(), required=False)
method = forms.CharField(widget=forms.HiddenInput(), required=False)
class WarnForm(BaseLogin):
"""
Bases: :class:`BaseLogin`
Form used on warn page before emiting a ticket
"""
#: ``True`` if the user has been warned of the ticket emission
warned = forms.BooleanField(widget=forms.HiddenInput(), required=False)
#: A valid LoginTicket to prevent POST replay
lt = forms.CharField(widget=forms.HiddenInput(), required=False)
class FederateSelect(BootsrapForm):
class FederateSelect(BaseLogin):
"""
Bases: :class:`django.forms.Form`
Bases: :class:`BaseLogin`
Form used on the login page when ``settings.CAS_FEDERATE`` is ``True``
allowing the user to choose an identity provider.
......@@ -76,39 +83,30 @@ class FederateSelect(BootsrapForm):
to_field_name="suffix",
label=_('Identity provider'),
)
#: The service url for which the user want a ticket
service = forms.CharField(label=_('service'), widget=forms.HiddenInput(), required=False)
method = forms.CharField(widget=forms.HiddenInput(), required=False)
#: A checkbox to ask to be warn before emiting a ticket for another service
warn = forms.BooleanField(
label=_('Warn me before logging me into other sites.'),
required=False
)
#: A checkbox to remember the user choices of :attr:`provider<FederateSelect.provider>`
remember = forms.BooleanField(label=_('Remember the identity provider'), required=False)
#: A checkbox to ask to be warn before emiting a ticket for another service
warn = forms.BooleanField(label=_('warn'), required=False)
#: Is the service asking the authentication renewal ?
renew = forms.BooleanField(widget=forms.HiddenInput(), required=False)
class UserCredential(BootsrapForm):