Merge pull request #12 from nitmir/dev

Update version to 0.7.0
This commit is contained in:
Valentin Samir 2016-08-24 23:09:40 +02:00 committed by GitHub
commit 902dd103f1
19 changed files with 737 additions and 134 deletions

420
CHANGELOG.rst Normal file
View File

@ -0,0 +1,420 @@
Change Log
##########
All notable changes to this project will be documented in this file.
.. contents:: Table of Contents
:depth: 2
v0.7.0 - 2016-08-24
===================
Added
-----
* Add a CHANGELOG.rst file.
* Add a validator to models CharField that should be regular expressions checking that user input
are valids regular expressions.
* Add a CAS_INFO_MESSAGES and CAS_INFO_MESSAGES_ORDER settings allowing to display messages in
info-boxes on the html pages of the default templates.
Changed
-------
* Allow the user defined CAS_COMPONENT_URLS to omit not changed values.
* replace code-block without language indication by literal blocks.
* Update french translation
Fixed
-----
* Some README.rst typos.
* some english typos
v0.6.4 - 2016-08-14
===================
commit: 282e3a831b3c0b0818881c2f16d056850d572b89
Added
-----
* Add a forgotten migration (only change help_text)
v0.6.3 - 2016-08-14
===================
commit: 07a537b403c5c5e39a4ddd084f90e3a4de88a54e
Added
-----
* Add powered by footer
* Add a github version badge
* documents templatetags
Changed
-------
* Usage of the documented API for models _meta in auth.DjangoAuthUser
* set warn cookie using javascript if possible
* Unfold many to many attributes in auth.DjangoAuthUser attributes
Fixed
-----
* typos in README.rst
* w3c validation
Cleaned
-------
* Code factorisation (models.py, views.py)
v0.6.2 - 2016-08-02
===================
commit: 773707e6c3c3fa20f697c946e31cafc591e8fee8
Added
-----
* Support authentication renewal in federate mode
* Add new version email and info box then new version is available
* Add SqlAuthUser and LdapAuthUser auth classes.
Deprecate the usage of MysqlAuthUser in favor of SqlAuthUser.
* Add pytest-warning to tests
* Add a checkbox to forget the identity provider if we checked "remember the identity provider"
* Add dependancies correspondance between python pypi, debian and centos packages in README
Changed
-------
* Move coverage computation last in travis
* Enable logging to stderr then running tests
* Remember "warn me before…" using a cookie
* Put favicon (shortcut icon) URL in settings
Deprecated
----------
* The auth class MysqlAuthUser is deprecated in favor of the SqlAuthUser class.
Fixed
-----
* Use custom templatetags instead settings custom attributes to Boundfields
(As it do not work with django 1.7)
* Display an error message on bad response from identity provider in federate mode
instead of crashing. (e.g. Bad XML document)
* Catch base64 decode error on b64decode to raise our custom exception BadHash
* Add secret as sensitive variables/post parameter for /auth
* Only set "remember my provider" in federated mode upon successful authentication
* Since we drop django-boostrap3 dependancies, Django default minimal version is 1.7.1
* [cas.py] Append renew=true when validating tickets
Cleaned
-------
* code factorization (cas.py, forms.py)
v0.6.1 - 2016-07-27
===================
commit: b168e0a6423c53de31aae6c444fa1d1c5083afa6
Added
-----
* Add sphinx docs + autodoc
* Add the possibility to run tests with "setup.py test"
* Include docs, Makefile, coverage config and tests config to source package
* Add serviceValidate ProxyTicket tests
* Add python 3.5 tox/travis tests
Changed
-------
* Use https://badges.genua.fr for badges
Fixed
-----
* Keep LoginTicket list upon fail authentication
(It prevent the next login attemps to fail because of bad LT)
Cleaned
-------
* Compact federated mode migration
* Reformat default_settings.py for documentation using sphinx autodoc
* Factorize some code (from views.py to Ticket models class methods)
* Update urlpattern for django 1.10
* Drop dependancies django-picklefield and django-bootstrap3
v0.6.0 - 2016-07-06
===================
commit: 4ad4d13baa4236c5cd72cc5216d7ff08dd361476
Added
-----
* Add a section describing service patterns options to README.rst
* Add a federation mode:
When the settings CAS_FEDERATE is True, django-cas-server will offer to the user to choose its
CAS backend to authenticate. Hence the login page do not display anymore a username/password form
but a select form with configured CASs backend.
This allow to give access to CAS supported applications to users from multiple organization
seamlessly.
It was originally developped to mach the need of https://ares.fr (Federated CAS at
https://cas.ares.fr, example of an application using it as https://chat.myares.fr)
Fixed
-----
* Then a ticket was marked as obtained with the user entering its credentials (aka not by SSO), and
the service did not require it, ticket validation was failing. Now, if the service do not require
authentication to be renewed, both ticket with renewed authentication and non renewed
authentication validate successfully.
v0.5.0 - 2016-07-01
===================
commit: e3ab64271b718a17e4cbbbabda0a2453107a83df
Added
-----
* Add more password scheme support to the mysql authentication backend: ldap user
attribute scheme encoding and simple password hash in hexa for md5, sha1, sha224,
sha256, sha384, sha512.
* Add a main heading to template "Central Authentication Service" with a logo controled
by CAS_LOGO_URL
* Add logos to the project (svg, png)
* Add coverage computation
* link project to codacy
* Update doc: add debian requirement, correct typos, correct links
Changed
-------
* Use settings to set tests username password and attributes
* Tweak the css and html for small screens
* Update travis cache for faster build
* clean Makefile, use pip to install, add target for tests
Fixed
-----
* Fix "warn me": we generate the ticket after the user agree to be connected to the service.
we were generating first and the connect button was a link to the service url with the ?ticket=
this could lead to situation where the ticket validity expire if the user is slow to click the
connect button.
* Fix authentication renewal: the renew parameter were not transmited when POST the login request
and self.renew (aks for auth renewal) was use instead of self.renewed (auth was renewd)
when generating a ticket.
* Fix attribute value replacement when generating a ticket: we were using the 'name' attribute
instead of the 'attribut' attribut on ReplaceAttributValue
* Fix attribute value replacement when generating a ticket then the value is a list: iterate over
each element of the list.
* Fix a NameError in utils.import_attr
* Fix serviceValidate and samlValidate when user_field is an attribute that is a list: we use
the first element of the list as username. we were serializing the list before that.
* Correct typos
Cleaned
-------
* Clean some useless conditional branches found with coverage
* Clean cas.js: use compact object declararion
* Use six for python{2|3} compatibility
* Move all unit tests to cas_server.tests and use django primitive. We also have a 100% tests
coverage now. Using the django classes for tests, we do not need to use our own dirty mock.
* Move mysql backend password check to a function in utils
v0.4.4 - 2016-04-30
===================
commit: 77d1607b0beefe8b171adcd8e2dcd974e3cdc72a
Added
-----
* Add sensitive_post_parameters and sensitive_variables for passwords, so passwords are anonymised
before django send an error report.
Fixed
-----
* Before commit 77fc5b5 the User model had a foreign key to the Session model. After the commit,
Only the session_key is store, allowing to use different backend than the Session SQL backend.
So the first migration (which is 21 migrations combined) was creating the User model with the
foreign key, then delete it and add the field session_key. Somehow, MySQL did not like it.
Now the first migration directly create the User model with the session_key and without the
foreign key to the Session SQL backend.
* Evaluate attributes variables in the template samlValidate.xml. the {{ }} was missing causing
the variable name to be displyed instead of the variable content.
* Return username in CAS 1.0 on the second ligne of the CAS response as specified.
Changed
-------
* Update tests
v0.4.3 - 2016-03-18
===================
commit: f6d436acb49f8d32b5457c316c18c4892accfd3b
Fixed
-----
* Currently, one of our dependancy, django-boostrap3, do not support django 1.7 in its last version.
So there is some detection of the current django installed version in setup.py to pin
django-boostrap3 to a version supported by django 1.7 if django 1.7 is installed, or to require
at least django 1.8.
The detection did not handle the case where django was not installed.
* [PEP8] Put line breaks after binary operator and not before.
v0.4.2 - 2016-03-18
===================
commit: d1cd17d6103281b03a8c57013671057eab80d21c
Added
-----
* On logout, display the number of sessions we are logged out from.
Fixed
-----
* One of our dependancy, django-boostrap3, do not support django 1.7 in its last version.
Some django version detection is added to setup.py to handle that.
* Some typos
* Make errors returned by utils.import_attr clearer (as they are likely to be displayed to the
django admin)
v0.4.1 - 2015-12-23
===================
commit: 5e63f39f9b7c678a300ad2f8132166be34d1d35b
Added
-----
* Add a run_test_server target to make file. Running make run_test_server will build a virtualenv,
create a django projet with django-cas-server and lauch ./management.py runserver. It is quite
handy to test developement version.
* Add verbose name for cas_server app and models
* Add Makefile clean targets for tox tests and test virtualenv.
* Add link on license badge to the GPLv3
Changed
-------
* Make Makefile clean targets modular
* Use img.shields.io for PyPi badges
* Get django-cas-server version in Makefile directly from setup.py (so now, the version is only
written in one place)
Fixed
-----
* Fix MysqlAuthUser when number of results != 1: In that case, call super anyway this the provided
username.
v0.4.0 - 2015-12-15
===================
commit: 7b4fac575449e50c2caff07f5798dba7f4e4857c
Added
-----
* Add a help_text to pattern of ServicePattern
* Add a timeout to SLO requests
* Add logging capabilities (see README.rst for instruction)
* Add management commands that should be called on a regular basis to README.rst
v0.3.5 - 2015-12-12
===================
commit: 51fa0861f550723171e52d58025fa789dccb8cde
Added
-----
* Add badges to README.rst
* Document settings parameter in README.rst
* Add a "Features" section in README.rst
Changed
-------
* Add a AuthUser auth class and use it as auth classes base class instead of DummyAuthUser
Fixed
-----
* Fix minor errors and typos in README.rst
v0.3.4 - 2015-12-12
===================
commit: 9fbfe19c550b147e8d0377108cdac8231cf0fb27
Added
-----
* Add static files, templates and locales to the PyPi release by adding them to MANIFEST.in
* Add a Makefile with the build/install/clean/dist targets
v0.3.3 - 2015-12-12
===================
commit: 16b700d0127abe33a1eabf5d5fe890aeb5167e5a
Added
-----
* Add management commands and migrations to the package by adding there packages to setup.py
packages list.
v0.3.2 - 2015-12-12 [YANKED]
============================
commit: eef9490885bf665a53349573ddb9cbe844319b3e
Added
-----
* Add migrations to setup.py package_data
v0.3.1 - 2015-12-12
===================
commit: d0f6ed9ea3a4b3e2bf715fd218c460892c32e39f
Added
-----
* Add a forgotten migration (remove auto_now_add=True from the User model)
v0.3.0 - 2015-12-12
===================
commit: b69769d71a99806a69e300eca0d7c6744a2b327e
Added
-----
* Django 1.9 compatibility (add tox and travis tests and fix some decrecated)
v0.2.1 - 2015-12-12
===================
commit: 90e077dedb991d651822e9bb283470de8bddd7dd
First github and PyPi release
Fixed
-----
* Prune .tox in MANIFEST.in
* add dist/ to .gitignore
* typo in setup.cfg
v0.2.0 - 2015-12-12 [YANKED]
============================
commit: a071ad46d7cd76fc97eb86f2f538d330457c6767
v0.1.0 - 2015-05-22 [YANKED]
============================
commit: 6981433bdf8a406992ba0c5e844a47d06ccc08fb

View File

@ -1,6 +1,7 @@
include tox.ini
include LICENSE
include README.rst
include CHANGELOG.rst
include .coveragerc
include Makefile
include pytest.ini
@ -15,6 +16,7 @@ include docs/conf.py
include docs/index.rst
include docs/Makefile
include docs/README.rst
include docs/CHANGELOG.rst
recursive-include docs/_ext *
recursive-include docs/package *
recursive-include docs/_static *

View File

@ -7,7 +7,7 @@ CAS Server is a Django application implementing the `CAS Protocol 3.0 Specificat
<https://apereo.github.io/cas/4.2.x/protocol/CAS-Protocol-Specification.html>`_.
By default, the authentication process use django internal users but you can easily
use any sources (see auth classes in the auth.py file)
use any sources (see the `Authentication backend`_ section and auth classes in the auth.py file)
.. contents:: Table of Contents
@ -38,7 +38,7 @@ Dependencies
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
Additionally, denpending of the `Authentication backend`_ you plan to use, you may need the following
python packages:
* ldap3
@ -174,13 +174,11 @@ Quick start
inactive since more than ``SESSION_COOKIE_AGE``. The default value for is ``1209600``
seconds (2 weeks). You probably should reduce it to something like ``86400`` seconds (1 day).
You could for example do as bellow :
You could for example do as bellow::
.. code-block::
0 0 * * * cas-user /path/to/project/manage.py clearsessions
*/5 * * * * cas-user /path/to/project/manage.py cas_clean_tickets
5 0 * * * cas-user /path/to/project/manage.py cas_clean_sessions
0 0 * * * cas-user /path/to/project/manage.py clearsessions
*/5 * * * * cas-user /path/to/project/manage.py cas_clean_tickets
5 0 * * * cas-user /path/to/project/manage.py cas_clean_sessions
5. Run ``python manage.py createsuperuser`` to create an administrator user.
@ -208,7 +206,7 @@ Template settings
Default is a key icon. Set it to ``False`` to disable it.
* ``CAS_SHOW_POWERED``: Set it to ``False`` to hide the powered by footer. The default is ``True``.
* ``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"``,
having the five following keys: ``"bootstrap3_css"``, ``"bootstrap3_js"``,
``"html5shiv"``, ``"respond"``, ``"jquery"``. The default is::
{
@ -219,6 +217,32 @@ Template settings
"jquery": "//code.jquery.com/jquery.min.js",
}
if you omit some keys of the dictionnary, the default value for these keys is used.
* ``CAS_INFO_MESSAGES``: Messages displayed in info-boxes on the html pages of the default templates.
It is a dictionnary mapping message name to a message dict. A message dict has 3 keys:
* ``message``: A unicode message to display, potentially wrapped around ugettex_lazy
* ``discardable``: A boolean, specify if the users can close the message info-box
* ``type``: One of info, success, info, warning, danger. The type of the info-box.
``CAS_INFO_MESSAGES`` contains by default one message, ``cas_explained``, which explain
roughly the purpose of a CAS. The default is::
{
"cas_explained": {
"message":_(
u"The Central Authentication Service grants you access to most of our websites by "
u"authenticating only once, so you don't need to type your credentials again unless "
u"your session expires or you logout."
),
"discardable": True,
"type": "info", # one of info, success, info, warning, danger
},
}
* ``CAS_INFO_MESSAGES_ORDER``: A list of message names. Order in which info-box messages are
displayed. Use an empty list to disable messages display. The default is ``[]``.
* ``CAS_LOGIN_TEMPLATE``: Path to the template showed on ``/login`` then the user
is not autenticated. The default is ``"cas_server/login.html"``.
* ``CAS_WARN_TEMPLATE``: Path to the template showed on ``/login?service=...`` then
@ -228,7 +252,7 @@ Template settings
authenticated. The default is ``"cas_server/logged.html"``.
* ``CAS_LOGOUT_TEMPLATE``: Path to the template showed on ``/logout`` then to user
is being disconnected. The default is ``"cas_server/logout.html"``
* ``CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT``: Should we redirect users to `/login` after they
* ``CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT``: Should we redirect users to ``/login`` after they
logged out instead of displaying ``CAS_LOGOUT_TEMPLATE``. The default is ``False``.
@ -271,7 +295,7 @@ 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
* ``CAS_NEW_VERSION_EMAIL_WARNING``: A boolean for sending a email to ``settings.ADMINS`` when a new
version is available. The default is ``True``.
@ -545,10 +569,10 @@ A service pattern has 4 associated models:
an email address to connect to it. To do so, put ``email`` in ``Attribute`` and ``.*`` in ``pattern``.
Then a user ask a ticket for a service, the service URL is compare against each service patterns
sorted by `position`. The first service pattern that matches the service URL is chosen.
Hence, you should give low `position` to very specific patterns like
``^https://www\.example\.com(/.*)?$`` and higher `position` to generic patterns like ``^https://.*``.
So the service URL `https://www.examle.com` will use the service pattern for
sorted by ``position``. The first service pattern that matches the service URL is chosen.
Hence, you should give low ``position`` to very specific patterns like
``^https://www\.example\.com(/.*)?$`` and higher ``position`` to generic patterns like ``^https://.*``.
So the service URL ``https://www.examle.com`` will use the service pattern for
``^https://www\.example\.com(/.*)?$`` and not the one for ``^https://.*``.
@ -572,7 +596,7 @@ An identity provider comes with 5 fields:
* ``Suffix``: the suffix that will be append to the username returned by the identity provider.
It must be unique.
* ``Server url``: the URL to the identity provider CAS. For instance, if you are using
``https://cas.example.org/login`` to authenticate on the CAS, the `server url` is
``https://cas.example.org/login`` to authenticate on the CAS, the ``server url`` is
``https://cas.example.org``
* ``CAS protocol version``: the version of the CAS protocol to use to contact the identity provider.
The default is version 3.
@ -593,11 +617,9 @@ Then using federate mode, you should add one command to a daily crontab: ``cas_c
This command clean the local cache of federated user from old unused users.
You could for example do as bellow :
You could for example do as bellow::
.. code-block::
10 0 * * * cas-user /path/to/project/manage.py cas_clean_federate
10 0 * * * cas-user /path/to/project/manage.py cas_clean_federate

View File

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

View File

@ -12,6 +12,7 @@
"""Default values for the app's settings"""
from django.conf import settings
from django.contrib.staticfiles.templatetags.staticfiles import static
from django.utils.translation import ugettext_lazy as _
from importlib import import_module
@ -180,13 +181,45 @@ CAS_NEW_VERSION_EMAIL_WARNING = True
#: You should not change it.
CAS_NEW_VERSION_JSON_URL = "https://pypi.python.org/pypi/django-cas-server/json"
#: Messages displayed in a info-box on the html pages of the default templates.
#: ``CAS_INFO_MESSAGES`` is a :class:`dict` mapping message name to a message :class:`dict`.
#: A message :class:`dict` has 3 keys:
#: * ``message``: A :class:`unicode`, the message to display, potentially wrapped around
#: ugettex_lazy
#: * ``discardable``: A :class:`bool`, specify if the users can close the message info-box
#: * ``type``: One of info, success, info, warning, danger. The type of the info-box.
#: ``CAS_INFO_MESSAGES`` contains by default one message, ``cas_explained``, which explain
#: roughly the purpose of a CAS.
CAS_INFO_MESSAGES = {
"cas_explained": {
"message": _(
u"The Central Authentication Service grants you access to most of our websites by "
u"authenticating only once, so you don't need to type your credentials again unless "
u"your session expires or you logout."
),
"discardable": True,
"type": "info", # one of info, success, info, warning, danger
},
}
#: :class:`list` of message names. Order in which info-box messages are displayed.
#: Let the list empty to disable messages display.
CAS_INFO_MESSAGES_ORDER = []
GLOBALS = globals().copy()
for name, default_value in GLOBALS.items():
# get the current setting value, falling back to default_value
value = getattr(settings, name, default_value)
# set the setting value to its value if defined, ellse to the default_value.
setattr(settings, name, value)
# only care about parameter begining by CAS_
if name.startswith("CAS_"):
# get the current setting value, falling back to default_value
value = getattr(settings, name, default_value)
# set the setting value to its value if defined, ellse to the default_value.
setattr(settings, name, value)
# Allow the user defined CAS_COMPONENT_URLS to omit not changed values
MERGED_CAS_COMPONENT_URLS = CAS_COMPONENT_URLS.copy()
MERGED_CAS_COMPONENT_URLS.update(settings.CAS_COMPONENT_URLS)
settings.CAS_COMPONENT_URLS = MERGED_CAS_COMPONENT_URLS
# if the federated mode is enabled, we must use the :class`cas_server.auth.CASFederateAuth` auth
# backend.

View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: cas_server\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-08-01 12:01+0200\n"
"PO-Revision-Date: 2016-08-01 12:01+0200\n"
"POT-Creation-Date: 2016-08-24 17:18+0200\n"
"PO-Revision-Date: 2016-08-24 17:18+0200\n"
"Last-Translator: Valentin Samir <valentin.samir@crans.org>\n"
"Language-Team: django <LL@li.org>\n"
"Language: fr\n"
@ -18,70 +18,81 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 1.8.8\n"
#: apps.py:25 templates/cas_server/base.html:9
#: templates/cas_server/base.html:27
#: apps.py:25 templates/cas_server/base.html:7
#: templates/cas_server/base.html:26
msgid "Central Authentication Service"
msgstr "Service Central d'Authentification"
#: forms.py:88
#: default_settings.py:197
msgid ""
"The Central Authentication Service grants you access to most of our websites "
"by authenticating only once, so you don't need to type your credentials "
"again unless your session expires or you logout."
msgstr ""
"Le Service Central d'Authentification permet, en vous authentifiant une "
"seule fois, d'accéder à la plupart de nos sites sans avoir à retaper votre "
"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."
#: forms.py:84
msgid "Identity provider"
msgstr "fournisseur d'identité"
#: forms.py:92 forms.py:111
#: forms.py:88 forms.py:107
msgid "Warn me before logging me into other sites."
msgstr "Prévenez-moi avant d'accéder à d'autres services."
#: forms.py:96
#: forms.py:92
msgid "Remember the identity provider"
msgstr "Se souvenir du fournisseur d'identité"
#: forms.py:106 models.py:600
#: forms.py:102 models.py:594
msgid "username"
msgstr "nom d'utilisateur"
#: forms.py:108
#: forms.py:104
msgid "password"
msgstr "mot de passe"
#: forms.py:130
#: forms.py:126
msgid "The credentials you provided cannot be determined to be authentic."
msgstr "Les informations transmises n'ont pas permis de vous authentifier."
#: forms.py:182
#: forms.py:178
msgid "User not found in the temporary database, please try to reconnect"
msgstr ""
"Utilisateur non trouvé dans la base de donnée temporaire, essayez de vous "
"reconnecter"
#: forms.py:196
#: forms.py:192
msgid "service"
msgstr "service"
#: management/commands/cas_clean_federate.py:20
msgid "Clean old federated users"
msgstr "Nettoyer les anciens utilisateurs fédéré"
msgstr "Nettoyer les anciens utilisateurs fédérés"
#: management/commands/cas_clean_sessions.py:22
msgid "Clean deleted sessions"
msgstr "Nettoyer les sessions supprimées"
#: management/commands/cas_clean_tickets.py:22
msgid "Clean old trickets"
msgid "Clean old tickets"
msgstr "Nettoyer les vieux tickets"
#: models.py:46
#: models.py:71
msgid "identity provider"
msgstr "fournisseur d'identité"
#: models.py:47
#: models.py:72
msgid "identity providers"
msgstr "fournisseurs d'identités"
#: models.py:53
#: models.py:78
msgid "suffix"
msgstr "suffixe"
#: models.py:55
#: models.py:80
msgid ""
"Suffix append to backend CAS returned username: ``returned_username`` @ "
"``suffix``."
@ -89,46 +100,46 @@ msgstr ""
"Suffixe ajouté au nom d'utilisateur retourné par le CAS du fournisseur "
"d'identité : `nom retourné`@`suffixe`."
#: models.py:62
#: models.py:87
msgid "server url"
msgstr "url du serveur"
#: models.py:72
#: models.py:97
msgid "CAS protocol version"
msgstr "Version du protocole CAS"
#: models.py:74
#: models.py:99
msgid ""
"Version of the CAS protocol to use when sending requests the the backend CAS."
msgstr ""
"Version du protocole CAS à utiliser lorsque l'on envoie des requête au CAS "
"du fournisseur d'identité."
#: models.py:81
#: models.py:106
msgid "verbose name"
msgstr "Nom du fournisseur"
#: models.py:82
#: models.py:107
msgid "Name for this identity provider displayed on the login page."
msgstr "Nom affiché pour ce fournisseur d'identité sur la page de connexion."
#: models.py:88 models.py:446
#: models.py:113 models.py:446
msgid "position"
msgstr "position"
#: models.py:102
#: models.py:127
msgid "display"
msgstr "afficher"
#: models.py:103
#: models.py:128
msgid "Display the provider on the login page."
msgstr "Afficher le fournisseur d'identité sur la page de connexion."
#: models.py:233
#: models.py:245
msgid "User"
msgstr "Utilisateur"
#: models.py:234
#: models.py:246
msgid "Users"
msgstr "Utilisateurs"
@ -149,7 +160,7 @@ msgstr "Motifs de services"
msgid "service patterns are sorted using the position attribute"
msgstr "Les motifs de service sont trié selon l'attribut position"
#: models.py:455 models.py:626
#: models.py:455 models.py:620
msgid "name"
msgstr "nom"
@ -157,7 +168,7 @@ msgstr "nom"
msgid "A name for the service"
msgstr "Un nom pour le service"
#: models.py:464 models.py:669 models.py:698
#: models.py:464 models.py:663 models.py:693
msgid "pattern"
msgstr "motif"
@ -172,108 +183,108 @@ msgstr ""
"expression rationnelle, les caractères spéciaux doivent être échappés avec "
"un '\\'."
#: models.py:476
#: models.py:477
msgid "user field"
msgstr "champ utilisateur"
#: models.py:477
#: models.py:478
msgid "Name of the attribute to transmit as username, empty = login"
msgstr ""
"Nom de l'attribut devant être transmis comme nom d'utilisateur au service. "
"vide = nom de connexion"
#: models.py:482
#: models.py:483
msgid "restrict username"
msgstr "limiter les noms d'utilisateurs"
#: models.py:483
#: models.py:484
msgid "Limit username allowed to connect to the list provided bellow"
msgstr ""
"Limiter les noms d'utilisateurs autorisé à se connecter à la liste fournie "
"ci-dessous"
#: models.py:488
#: models.py:489
msgid "proxy"
msgstr "proxy"
#: models.py:489
#: models.py:490
msgid "Proxy tickets can be delivered to the service"
msgstr "des proxy tickets peuvent être délivrés au service"
#: models.py:495
#: models.py:496
msgid "proxy callback"
msgstr ""
#: models.py:496
#: models.py:497
msgid "can be used as a proxy callback to deliver PGT"
msgstr "peut être utilisé comme un callback pour recevoir un PGT"
#: models.py:503
#: models.py:504
msgid "single log out"
msgstr ""
#: models.py:504
#: models.py:505
msgid "Enable SLO for the service"
msgstr "Active le SLO pour le service"
#: models.py:512
#: models.py:513
msgid "single log out callback"
msgstr ""
#: models.py:513
#: models.py:514
msgid ""
"URL where the SLO request will be POST. empty = service url\n"
"This is usefull for non HTTP proxied services."
msgstr ""
"URL a laquelle la requête de déconnexion sera postée. vide = l'url du "
"URL à laquelle la requête de déconnexion sera postée. vide = l'url du "
"service\n"
"Ceci n'est utilise que pour des services non HTTP proxifiés"
"Ceci n'est en général utilisé que pour des services non HTTP proxifiés"
#: models.py:601
#: models.py:595
msgid "username allowed to connect to the service"
msgstr "noms d'utilisateurs autorisé à se connecter au service"
msgstr "noms d'utilisateurs autorisés à se connecter au service"
#: models.py:627
#: models.py:621
msgid "name of an attribute to send to the service, use * for all attributes"
msgstr ""
"nom d'un attribut a envoyer au service, utiliser * pour tous les attributs"
"nom d'un attribut à envoyer au service, utiliser * pour tous les attributs"
#: models.py:634 models.py:705
#: models.py:628 models.py:701
msgid "replace"
msgstr "remplacement"
#: models.py:635
#: models.py:629
msgid ""
"name under which the attribute will be showto the service. empty = default "
"name under which the attribute will be show to the service. empty = default "
"name of the attribut"
msgstr ""
"nom sous lequel l'attribut sera rendu visible au service. vide = inchangé"
#: models.py:662 models.py:692
#: models.py:656 models.py:687
msgid "attribute"
msgstr "attribut"
#: models.py:663
#: models.py:657
msgid "Name of the attribute which must verify pattern"
msgstr "Nom de l'attribut devant vérifier un motif"
#: models.py:670
#: models.py:664
msgid "a regular expression"
msgstr "une expression régulière"
#: models.py:693
#: models.py:688
msgid "Name of the attribute for which the value must be replace"
msgstr "nom de l'attribut pour lequel la valeur doit être remplacé"
msgstr "Nom de l'attribut pour lequel la valeur doit être remplacé"
#: models.py:699
#: models.py:694
msgid "An regular expression maching whats need to be replaced"
msgstr "une expression régulière reconnaissant ce qui doit être remplacé"
msgstr "Une expression régulière reconnaissant ce qui doit être remplacé"
#: models.py:706
#: models.py:702
msgid "replace expression, groups are capture by \\1, \\2 …"
msgstr "expression de remplacement, les groupe sont capturé par \\1, \\2"
#: templates/cas_server/base.html:38
#: templates/cas_server/base.html:43
#, python-format
msgid ""
"A new version of the application is available. This instance runs "
@ -282,7 +293,7 @@ msgid ""
msgstr ""
"Une nouvelle version de l'application est disponible. Cette instance utilise "
"la version %(VERSION)s et la dernière version est %(LAST_VERSION)s. Merci de "
"vous mettre a jour."
"vous mettre à jour."
#: templates/cas_server/logged.html:4
msgid ""
@ -291,10 +302,10 @@ msgid ""
"your web browser when you are done accessing services that require "
"authentication!"
msgstr ""
"<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 "
"navigateur après avoir fini d'accéder a des services demandant une "
"authentification !"
"<h3>Connexion réussie</h3>Vous vous êtes connecté(e) auprès du Service "
"Central d'Authentification.<br/>Pour des raisons de sécurité, veuillez vous "
"déconnecter et fermer votre navigateur après avoir fini d'accéder à des "
"services demandant une authentification !"
#: templates/cas_server/logged.html:8
msgid "Log me out from all my sessions"
@ -310,7 +321,7 @@ msgstr "Se déconnecter"
#: templates/cas_server/login.html:6
msgid "Please log in"
msgstr "Merci de se connecter"
msgstr "Veuillez vous authentifier"
#: templates/cas_server/login.html:14
msgid "Login"
@ -320,7 +331,12 @@ msgstr "Connexion"
msgid "Connect to the service"
msgstr "Se connecter au service"
#: views.py:168
#: utils.py:736
#, python-format
msgid "\"%(value)s\" is not a valid regular expression"
msgstr "\"%(value)s\" n'est pas une expression rationnelle valide"
#: views.py:185
msgid ""
"<h3>Logout successful</h3>You have successfully logged out from the Central "
"Authentication Service. For security reasons, exit your web browser."
@ -329,7 +345,7 @@ msgstr ""
"d'Authentification. Pour des raisons de sécurité, veuillez fermer votre "
"navigateur."
#: views.py:174
#: views.py:191
#, python-format
msgid ""
"<h3>Logout successful</h3>You have successfully logged out from %s sessions "
@ -340,7 +356,7 @@ msgstr ""
"Service Central d'Authentification. Pour des raisons de sécurité, veuillez "
"fermer votre navigateur."
#: views.py:181
#: views.py:198
msgid ""
"<h3>Logout successful</h3>You were already logged out from the Central "
"Authentication Service. For security reasons, exit your web browser."
@ -349,7 +365,7 @@ msgstr ""
"d'Authentification. Pour des raisons de sécurité, veuillez fermer votre "
"navigateur."
#: views.py:361
#: views.py:378
#, python-format
msgid ""
"Invalid response from your identity provider CAS upon ticket %(ticket)s "
@ -358,48 +374,48 @@ msgstr ""
"Réponse invalide du CAS du fournisseur d'identité lors de la validation du "
"ticket %(ticket)s: %(error)r"
#: views.py:483
#: views.py:500
msgid "Invalid login ticket, please retry to login"
msgstr "Ticket de connexion invalide, merci de réessayé de vous connecter"
#: views.py:675
#: views.py:692
#, python-format
msgid "Authentication has been required by service %(name)s (%(url)s)"
msgstr ""
"Une demande d'authentification a été émise pour le service %(name)s "
"(%(url)s)."
#: views.py:713
#: views.py:730
#, python-format
msgid "Service %(url)s non allowed."
msgid "Service %(url)s not allowed."
msgstr "le service %(url)s n'est pas autorisé."
#: views.py:720
msgid "Username non allowed"
#: views.py:737
msgid "Username not allowed"
msgstr "Nom d'utilisateur non authorisé"
#: views.py:727
msgid "User characteristics non allowed"
#: views.py:744
msgid "User characteristics not allowed"
msgstr "Caractéristique utilisateur non autorisée"
#: views.py:734
#: views.py:751
#, python-format
msgid "The attribute %(field)s is needed to use that service"
msgstr "L'attribut %(field)s est nécessaire pour se connecter à ce service"
#: views.py:824
#: views.py:841
#, python-format
msgid "Authentication renewal required by service %(name)s (%(url)s)."
msgstr "Demande de réauthentification pour le service %(name)s (%(url)s)."
#: views.py:831
#: views.py:848
#, python-format
msgid "Authentication required by service %(name)s (%(url)s)."
msgstr "Authentification requise par le service %(name)s (%(url)s)."
#: views.py:838
#: views.py:855
#, python-format
msgid "Service %s non allowed"
msgid "Service %s not allowed"
msgstr "Le service %s n'est pas autorisé"
#~ msgid "Logged"

View File

@ -19,7 +19,7 @@ from ... import models
class Command(BaseCommand):
"""Clean old trickets"""
args = ''
help = _(u"Clean old trickets")
help = _(u"Clean old tickets")
def handle(self, *args, **options):
models.User.clean_old_entries()

View File

@ -466,7 +466,8 @@ class ServicePattern(models.Model):
"A regular expression matching services. "
"Will usually looks like '^https://some\\.server\\.com/path/.*$'."
"As it is a regular expression, special character must be escaped with a '\\'."
)
),
validators=[utils.regexpr_validator]
)
#: Name of the attribute to transmit as username, if empty the user login is used
user_field = models.CharField(
@ -625,7 +626,7 @@ class ReplaceAttributName(models.Model):
max_length=255,
blank=True,
verbose_name=_(u"replace"),
help_text=_(u"name under which the attribute will be show"
help_text=_(u"name under which the attribute will be show "
u"to the service. empty = default name of the attribut")
)
#: ForeignKey to a :class:`ServicePattern`. :class:`ReplaceAttributName` instances for a
@ -660,7 +661,8 @@ class FilterAttributValue(models.Model):
pattern = models.CharField(
max_length=255,
verbose_name=_(u"pattern"),
help_text=_(u"a regular expression")
help_text=_(u"a regular expression"),
validators=[utils.regexpr_validator]
)
#: ForeignKey to a :class:`ServicePattern`. :class:`FilterAttributValue` instances for a
#: :class:`ServicePattern` are accessible thought its :attr:`ServicePattern.filters`
@ -689,7 +691,8 @@ class ReplaceAttributValue(models.Model):
pattern = models.CharField(
max_length=255,
verbose_name=_(u"pattern"),
help_text=_(u"An regular expression maching whats need to be replaced")
help_text=_(u"An regular expression maching whats need to be replaced"),
validators=[utils.regexpr_validator]
)
#: The replacement to what is mached by :attr:`pattern`. groups are capture by \\1, \\2 …
replace = models.CharField(

View File

@ -31,14 +31,14 @@ function eraseCookie(name) {
createCookie(name,"",-1);
}
function alert_version(last_version){
function discard_and_remember(id, cookie_name, token, days=10*365){
jQuery(function( $ ){
$("#alert-version").click(function( e ){
$(id).click(function( e ){
e.preventDefault();
createCookie("cas-alert-version", last_version, 10*365);
createCookie(cookie_name, token, days);
});
if(readCookie("cas-alert-version") === last_version){
$("#alert-version").parent().hide();
if(readCookie(cookie_name) === token){
$(id).parent().hide();
}
});
}

View File

@ -31,10 +31,16 @@
<div class="col-lg-3 col-md-3 col-sm-2 col-xs-12"></div>
<div class="col-lg-6 col-md-6 col-sm-8 col-xs-12">
{% if auto_submit %}<noscript>{% endif %}
{% for msg in CAS_INFO_RENDER %}
<div class="alert alert-{{msg.type}}{% if msg.discardable %} alert-dismissable{% endif %}">
{% if msg.discardable %}<button type="button" class="close" data-dismiss="alert" aria-hidden="true" id="info-{{msg.name}}">&#215;</button>{% endif %}
<p>{{msg.message}}</p>
</div>
{% endfor %}
{% if settings.CAS_NEW_VERSION_HTML_WARNING and upgrade_available %}
<div class="alert alert-info alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true" id="alert-version">&#215;</button>
{% blocktrans %}A new version of the application is available. This instance runs {{VERSION}} and the last version is {{LAST_VERSION}}. Please consider upgrading.{% endblocktrans %}
<p>{% blocktrans %}A new version of the application is available. This instance runs {{VERSION}} and the last version is {{LAST_VERSION}}. Please consider upgrading.{% endblocktrans %}</p>
</div>
{% endif %}
{% block ante_messages %}{% endblock %}
@ -52,7 +58,7 @@
class="alert alert-danger"
{% endif %}
{% endspaceless %}>
{{message|safe}}
<p>{{message|safe}}</p>
</div>
{% endfor %}
{% if auto_submit %}</noscript>{% endif %}
@ -71,9 +77,17 @@
<script src="{{settings.CAS_COMPONENT_URLS.jquery}}"></script>
<script src="{{settings.CAS_COMPONENT_URLS.bootstrap3_js}}"></script>
<script src="{% static "cas_server/functions.js" %}"></script>
{% if settings.CAS_NEW_VERSION_HTML_WARNING and upgrade_available %}
<script type="text/javascript">alert_version("{{LAST_VERSION}}")</script>
{% endif %}
<script type="text/javascript">
{% if settings.CAS_NEW_VERSION_HTML_WARNING and upgrade_available %}
discard_and_remember("#alert-version", "cas-alert-version", "{{LAST_VERSION}}");
{% endif %}
{% for msg in CAS_INFO_RENDER %}
{% if msg.discardable %}
discard_and_remember("#info-{{msg.name}}", "cas-info-{{msg.name}}", "{{msg.hash}}");
{% endif %}
{% endfor %}
{% block javascript_inline %}{% endblock %}
</script>
{% block javascript %}{% endblock %}
</body>
</html>

View File

@ -15,7 +15,7 @@
{% if auto_submit %}</noscript>{% endif %}
</form>
{% endblock %}
{% block javascript %}<script type="text/javascript">
{% block javascript_inline %}
jQuery(function( $ ){
$("#id_warn").click(function(e){
if($("#id_warn").is(':checked')){
@ -26,5 +26,5 @@ jQuery(function( $ ){
});
});{% if auto_submit %}
document.getElementById('login_form').submit(); // SUBMIT FORM{% endif %}
</script>{% endblock %}
{% endblock %}

View File

@ -255,3 +255,9 @@ class UtilsTestCase(TestCase):
self.assertIsInstance(result, dict)
self.assertIn('applied', result)
self.assertIsInstance(result['applied'], datetime.datetime)
def test_regexpr_validator(self):
"""test the function regexpr_validator"""
utils.regexpr_validator("^a$")
with self.assertRaises(utils.ValidationError):
utils.regexpr_validator("[")

View File

@ -75,6 +75,45 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin):
response = client.get("/login")
self.assertNotIn(b"A new version of the application is available", response.content)
@override_settings(CAS_INFO_MESSAGES_ORDER=["cas_explained"])
def test_messages_info_box_enabled(self):
"""test that the message info-box is displayed then enabled"""
client = Client()
response = client.get("/login")
self.assertIn(
b"The Central Authentication Service grants you access to most of our websites by ",
response.content
)
@override_settings(CAS_INFO_MESSAGES_ORDER=[])
def test_messages_info_box_disabled(self):
"""test that the message info-box is not displayed then disabled"""
client = Client()
response = client.get("/login")
self.assertNotIn(
b"The Central Authentication Service grants you access to most of our websites by ",
response.content
)
# test1 and test2 are malformed and should be ignored, test3 is ok, test5 do not
# exists and should be ignored
@override_settings(CAS_INFO_MESSAGES_ORDER=["test1", "test2", "test3", "test5"])
@override_settings(CAS_INFO_MESSAGES={
"test1": "test", # not a dict, should be ignored
"test2": {"type": "success"}, # not "message" key, should be ignored
"test3": {"message": "test3"},
"test4": {"message": "test4"},
})
def test_messages_info_box_bad_messages(self):
"""test that mal formated messages dict are ignored"""
client = Client()
# not errors should be raises
response = client.get("/login")
# test3 is ok est should be there
self.assertIn(b"test3", response.content)
# test4 is not in CAS_INFO_MESSAGES_ORDER and should not be there
self.assertNotIn(b"test4", response.content)
def test_login_view_post_goodpass_goodlt(self):
"""Test a successul login"""
# we get a client who fetch a frist time the login page and the login form default
@ -234,7 +273,7 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin):
response = client.get("/login?service=https://www.example.net")
self.assertEqual(response.status_code, 200)
# we warn the user that https://www.example.net is not an allowed service url
self.assertTrue(b"Service https://www.example.net non allowed" in response.content)
self.assertTrue(b"Service https://www.example.net not allowed" in response.content)
def test_view_login_get_auth_allowed_service(self):
"""Request a ticket for an allowed service by an authenticated client"""
@ -280,7 +319,7 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin):
self.assertEqual(response.status_code, 200)
# we warn the user that https://www.example.net is not an allowed service url
# NO ticket are created
self.assertTrue(b"Service https://www.example.org non allowed" in response.content)
self.assertTrue(b"Service https://www.example.org not allowed" in response.content)
def test_user_logged_not_in_db(self):
"""If the user is logged but has been delete from the database, it should be logged out"""
@ -314,7 +353,7 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin):
response = client.get("/login", {'service': self.service_restrict_user_fail})
self.assertEqual(response.status_code, 200)
# the ticket is not created and a warning is displayed to the user
self.assertTrue(b"Username non allowed" in response.content)
self.assertTrue(b"Username not allowed" in response.content)
# same but with the tes user username being one of the allowed usernames
response = client.get("/login", {'service': self.service_restrict_user_success})
@ -337,7 +376,7 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin):
response = client.get("/login", {'service': service})
# the ticket is not created and a warning is displayed to the user
self.assertEqual(response.status_code, 200)
self.assertTrue(b"User characteristics non allowed" in response.content)
self.assertTrue(b"User characteristics not allowed" in response.content)
# same but with rectriction that a valid upon the test user attributes
response = client.get("/login", {'service': self.service_filter_success})
@ -546,7 +585,7 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin):
self.assertEqual(data["messages"][0]["level"], "error")
self.assertEqual(
data["messages"][0]["message"],
"Service https://www.example.org non allowed."
"Service https://www.example.org not allowed."
)
@override_settings(CAS_ENABLE_AJAX_AUTH=True)

View File

@ -18,7 +18,10 @@ from django.contrib import messages
from django.contrib.messages import constants as DEFAULT_MESSAGE_LEVELS
from django.core.serializers.json import DjangoJSONEncoder
from django.utils import timezone
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
import re
import random
import string
import json
@ -61,6 +64,7 @@ def context(params):
"""
params["settings"] = settings
params["message_levels"] = DEFAULT_MESSAGE_LEVELS
if settings.CAS_NEW_VERSION_HTML_WARNING:
LAST_VERSION = last_version()
params["VERSION"] = VERSION
@ -69,6 +73,27 @@ def context(params):
params["upgrade_available"] = decode_version(VERSION) < decode_version(LAST_VERSION)
else:
params["upgrade_available"] = False
if settings.CAS_INFO_MESSAGES_ORDER:
params["CAS_INFO_RENDER"] = []
for msg_name in settings.CAS_INFO_MESSAGES_ORDER:
if msg_name in settings.CAS_INFO_MESSAGES:
if not isinstance(settings.CAS_INFO_MESSAGES[msg_name], dict):
continue
msg = settings.CAS_INFO_MESSAGES[msg_name].copy()
if "message" in msg:
msg["name"] = msg_name
# use info as default infox type
msg["type"] = msg.get("type", "info")
# make box discardable by default
msg["discardable"] = msg.get("discardable", True)
msg_hash = (
six.text_type(msg["message"]).encode("utf-8") +
msg["type"].encode("utf-8")
)
# hash depend of the rendering language
msg["hash"] = hashlib.md5(msg_hash).hexdigest()
params["CAS_INFO_RENDER"].append(msg)
return params
@ -700,3 +725,19 @@ def logout_request(ticket):
'datetime': timezone.now().isoformat(),
'ticket': ticket
}
def regexpr_validator(value):
"""
Test that ``value`` is a valid regular expression
:param unicode value: A regular expression to test
:raises ValidationError: if ``value`` is not a valid regular expression
"""
try:
re.compile(value)
except re.error:
raise ValidationError(
_('"%(value)s" is not a valid regular expression'),
params={'value': value}
)

View File

@ -727,21 +727,21 @@ class LoginView(View, LogoutMixin):
messages.add_message(
self.request,
messages.ERROR,
_(u'Service %(url)s non allowed.') % {'url': self.service}
_(u'Service %(url)s not allowed.') % {'url': self.service}
)
except models.BadUsername:
error = 2
messages.add_message(
self.request,
messages.ERROR,
_(u"Username non allowed")
_(u"Username not allowed")
)
except models.BadFilter:
error = 3
messages.add_message(
self.request,
messages.ERROR,
_(u"User characteristics non allowed")
_(u"User characteristics not allowed")
)
except models.UserFieldNotDefined:
error = 4
@ -852,7 +852,7 @@ class LoginView(View, LogoutMixin):
messages.add_message(
self.request,
messages.ERROR,
_(u'Service %s non allowed') % self.service
_(u'Service %s not allowed') % self.service
)
if self.ajax:
data = {

1
docs/CHANGELOG.rst Normal file
View File

@ -0,0 +1 @@
.. include:: ../CHANGELOG.rst

View File

@ -14,6 +14,11 @@ Contents:
README
package/cas_server
.. toctree::
:maxdepth: 2
CHANGELOG
Indices and tables
==================

View File

@ -101,6 +101,7 @@ deps=
skip_install=True
commands=
rst2html.py --strict {toxinidir}/README.rst /dev/null
rst2html.py --halt=warning {toxinidir}/CHANGELOG.rst /dev/null
{[post_cmd]commands}
whitelist_externals={[post_cmd]whitelist_externals}