Merge pull request #34 from nitmir/dev
Update version to 0.9.0 v0.9.0 - 2017-11-17 =================== Added ----- * Dutch translation * Protuguese translation (brazilian variant) * Support for ldap3 version 2 or more (changes in the API) All exception are now in ldap3.core.exceptions, methodes for fetching attritutes and dn are renamed. * Possibility to disable service message boxes on the login pages Fixed ----- * Then using the LDAP auth backend with ``bind`` method for password check, do not try to bind if the user dn was not found. This was causing the exception ``'NoneType' object has no attribute 'getitem'`` describe in #21 * Increase the max size of usernames (30 chars to 250) * Fix XSS js injection
This commit is contained in:
		| @@ -6,6 +6,29 @@ 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.9.0 - 2017-11-17 | ||||||
|  | =================== | ||||||
|  |  | ||||||
|  | Added | ||||||
|  | ----- | ||||||
|  | * Dutch translation | ||||||
|  | * Protuguese translation (brazilian variant) | ||||||
|  | * Support for ldap3 version 2 or more (changes in the API) | ||||||
|  |   All exception are now in ldap3.core.exceptions, methodes for fetching attritutes and | ||||||
|  |   dn are renamed. | ||||||
|  | * Possibility to disable service message boxes on the login pages | ||||||
|  |  | ||||||
|  | Fixed | ||||||
|  | ----- | ||||||
|  | * Then using the LDAP auth backend with ``bind`` method for password check, do not try to bind | ||||||
|  |   if the user dn was not found. This was causing the exception | ||||||
|  |   ``'NoneType' object has no attribute 'getitem'`` describe in #21 | ||||||
|  | * Increase the max size of usernames (30 chars to 250) | ||||||
|  | * Fix XSS js injection | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| v0.8.0 - 2017-03-08 | v0.8.0 - 2017-03-08 | ||||||
| =================== | =================== | ||||||
|  |  | ||||||
|   | |||||||
| @@ -218,7 +218,8 @@ Template settings | |||||||
|         } |         } | ||||||
|  |  | ||||||
|   if you omit some keys of the dictionnary, the default value for these keys is used. |   if you omit some keys of the dictionnary, the default value for these keys is used. | ||||||
|  | * ``CAS_SHOW_SERVICE_MESSAGES``: Messages displayed about the state of the service on the login page. | ||||||
|  |   The default is ``True``. | ||||||
| * ``CAS_INFO_MESSAGES``: Messages displayed in info-boxes on the html pages of the default templates. | * ``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: |   It is a dictionnary mapping message name to a message dict. A message dict has 3 keys: | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
| """A django CAS server application""" | """A django CAS server application""" | ||||||
|  |  | ||||||
| #: version of the application | #: version of the application | ||||||
| VERSION = '0.8.0' | VERSION = '0.9.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' | ||||||
|   | |||||||
| @@ -27,6 +27,7 @@ except ImportError: | |||||||
|  |  | ||||||
| try:  # pragma: no cover | try:  # pragma: no cover | ||||||
|     import ldap3 |     import ldap3 | ||||||
|  |     import ldap3.core.exceptions | ||||||
| except ImportError: | except ImportError: | ||||||
|     ldap3 = None |     ldap3 = None | ||||||
|  |  | ||||||
| @@ -297,9 +298,19 @@ class LdapAuthUser(DBAuthUser):  # pragma: no cover | |||||||
|                     settings.CAS_LDAP_USER_QUERY % ldap3.utils.conv.escape_bytes(username), |                     settings.CAS_LDAP_USER_QUERY % ldap3.utils.conv.escape_bytes(username), | ||||||
|                     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() |                     # try the new ldap3>=2 API | ||||||
|                     # store the user dn |                     try: | ||||||
|                     user["dn"] = conn.entries[0].entry_get_dn() |                         user = conn.entries[0].entry_attributes_as_dict | ||||||
|  |                         # store the user dn | ||||||
|  |                         user["dn"] = conn.entries[0].entry_dn | ||||||
|  |                     # fallback to ldap3<2 API | ||||||
|  |                     except ( | ||||||
|  |                         ldap3.core.exceptions.LDAPKeyError,  # ldap3<1 exception | ||||||
|  |                         ldap3.core.exceptions.LDAPAttributeError  # ldap3<2 exception | ||||||
|  |                     ): | ||||||
|  |                         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,7 +319,7 @@ class LdapAuthUser(DBAuthUser):  # pragma: no cover | |||||||
|                 else: |                 else: | ||||||
|                     super(LdapAuthUser, self).__init__(username) |                     super(LdapAuthUser, self).__init__(username) | ||||||
|                 break |                 break | ||||||
|             except ldap3.LDAPCommunicationError: |             except ldap3.core.exceptions.LDAPCommunicationError: | ||||||
|                 if retry_nb == 2: |                 if retry_nb == 2: | ||||||
|                     raise |                     raise | ||||||
|  |  | ||||||
| @@ -321,7 +332,7 @@ class LdapAuthUser(DBAuthUser):  # pragma: no cover | |||||||
|                 correct, ``False`` otherwise. |                 correct, ``False`` otherwise. | ||||||
|             :rtype: bool |             :rtype: bool | ||||||
|         """ |         """ | ||||||
|         if settings.CAS_LDAP_PASSWORD_CHECK == "bind": |         if self.user and settings.CAS_LDAP_PASSWORD_CHECK == "bind": | ||||||
|             try: |             try: | ||||||
|                 conn = ldap3.Connection( |                 conn = ldap3.Connection( | ||||||
|                     settings.CAS_LDAP_SERVER, |                     settings.CAS_LDAP_SERVER, | ||||||
| @@ -336,8 +347,18 @@ class LdapAuthUser(DBAuthUser):  # pragma: no cover | |||||||
|                         settings.CAS_LDAP_USER_QUERY % ldap3.utils.conv.escape_bytes(self.username), |                         settings.CAS_LDAP_USER_QUERY % ldap3.utils.conv.escape_bytes(self.username), | ||||||
|                         attributes=ldap3.ALL_ATTRIBUTES |                         attributes=ldap3.ALL_ATTRIBUTES | ||||||
|                     ) and len(conn.entries) == 1: |                     ) and len(conn.entries) == 1: | ||||||
|                         attributes = conn.entries[0].entry_get_attributes_dict() |                         # try the ldap3>=2 API | ||||||
|                         attributes["dn"] = conn.entries[0].entry_get_dn() |                         try: | ||||||
|  |                             attributes = conn.entries[0].entry_attributes_as_dict | ||||||
|  |                             # store the user dn | ||||||
|  |                             attributes["dn"] = conn.entries[0].entry_dn | ||||||
|  |                         # fallback to ldap<2 API | ||||||
|  |                         except ( | ||||||
|  |                             ldap3.core.exceptions.LDAPKeyError,  # ldap3<1 exception | ||||||
|  |                             ldap3.core.exceptions.LDAPAttributeError  # ldap3<2 exception | ||||||
|  |                         ): | ||||||
|  |                             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 |                         # cache the attributes locally as we wont have access to the user password | ||||||
|                         # later. |                         # later. | ||||||
|                         user = UserAttributes.objects.get_or_create(username=self.username)[0] |                         user = UserAttributes.objects.get_or_create(username=self.username)[0] | ||||||
| @@ -346,7 +367,10 @@ class LdapAuthUser(DBAuthUser):  # pragma: no cover | |||||||
|                 finally: |                 finally: | ||||||
|                     conn.unbind() |                     conn.unbind() | ||||||
|                 return True |                 return True | ||||||
|             except (ldap3.LDAPBindError, ldap3.LDAPCommunicationError): |             except ( | ||||||
|  |                 ldap3.core.exceptions.LDAPBindError, | ||||||
|  |                 ldap3.core.exceptions.LDAPCommunicationError | ||||||
|  |             ): | ||||||
|                 return False |                 return False | ||||||
|         elif self.user and self.user.get(settings.CAS_LDAP_PASSWORD_ATTR): |         elif self.user and self.user.get(settings.CAS_LDAP_PASSWORD_ATTR): | ||||||
|             return check_password( |             return check_password( | ||||||
|   | |||||||
| @@ -185,6 +185,8 @@ CAS_NEW_VERSION_EMAIL_WARNING = True | |||||||
| #: You should not change it. | #: You should not change it. | ||||||
| CAS_NEW_VERSION_JSON_URL = "https://pypi.python.org/pypi/django-cas-server/json" | CAS_NEW_VERSION_JSON_URL = "https://pypi.python.org/pypi/django-cas-server/json" | ||||||
|  |  | ||||||
|  | #: If the service message should be displayed on the login page | ||||||
|  | CAS_SHOW_SERVICE_MESSAGES = True | ||||||
|  |  | ||||||
| #: Messages displayed in a info-box on the html pages of the default templates. | #: 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`. | #: ``CAS_INFO_MESSAGES`` is a :class:`dict` mapping message name to a message :class:`dict`. | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								cas_server/locale/pt_BR/django.mo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								cas_server/locale/pt_BR/django.mo
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										398
									
								
								cas_server/locale/pt_BR/django.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										398
									
								
								cas_server/locale/pt_BR/django.po
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,398 @@ | |||||||
|  | # SOME DESCRIPTIVE TITLE.  | ||||||
|  | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER | ||||||
|  | # This file is distributed under the same license as the PACKAGE package. | ||||||
|  | # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. | ||||||
|  | # | ||||||
|  | msgid "" | ||||||
|  | msgstr "" | ||||||
|  | "Project-Id-Version: \n" | ||||||
|  | "Report-Msgid-Bugs-To: \n" | ||||||
|  | "POT-Creation-Date: 2017-08-22 08:18-0300\n" | ||||||
|  | "PO-Revision-Date: 2017-08-29 18:09+0200\n" | ||||||
|  | "Language-Team: Roberto Morati <robertomorati@gmail.com>\n" | ||||||
|  | "Language: pt_BR\n" | ||||||
|  | "MIME-Version: 1.0\n" | ||||||
|  | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
|  | "Content-Transfer-Encoding: 8bit\n" | ||||||
|  | "Plural-Forms: nplurals=2; plural=(n > 1);\n" | ||||||
|  | "Last-Translator: Valentin Samir <valentin.samir@crans.org>\n" | ||||||
|  | "X-Generator: Poedit 1.8.11\n" | ||||||
|  |  | ||||||
|  | #: cas_server/apps.py:25 cas_server/templates/cas_server/base.html:7 | ||||||
|  | #: cas_server/templates/cas_server/base.html:26 | ||||||
|  | msgid "Central Authentication Service" | ||||||
|  | msgstr "Central de Autenticação de Serviços" | ||||||
|  |  | ||||||
|  | #: cas_server/default_settings.py:201 | ||||||
|  | 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 "" | ||||||
|  | "A Central de Autenticação de Serviços garante seu acesso à maioria dos nossos sitespor " | ||||||
|  | "meio de uma única autenticação, então você não precisa digitar suas " | ||||||
|  | "credenciaisnovamente, ao menos que sua sessão expire ou seu logout." | ||||||
|  |  | ||||||
|  | #: cas_server/forms.py:85 | ||||||
|  | msgid "Identity provider" | ||||||
|  | msgstr "Provedor de identidade" | ||||||
|  |  | ||||||
|  | #: cas_server/forms.py:89 cas_server/forms.py:111 | ||||||
|  | msgid "Warn me before logging me into other sites." | ||||||
|  | msgstr "Avise-me antes de me registrar em outros sites" | ||||||
|  |  | ||||||
|  | #: cas_server/forms.py:93 | ||||||
|  | msgid "Remember the identity provider" | ||||||
|  | msgstr "Relembrar o provedor de identidade" | ||||||
|  |  | ||||||
|  | #: cas_server/forms.py:104 cas_server/models.py:638 | ||||||
|  | msgid "username" | ||||||
|  | msgstr "usuário" | ||||||
|  |  | ||||||
|  | #: cas_server/forms.py:108 | ||||||
|  | msgid "password" | ||||||
|  | msgstr "senha" | ||||||
|  |  | ||||||
|  | #: cas_server/forms.py:131 | ||||||
|  | msgid "The credentials you provided cannot be determined to be authentic." | ||||||
|  | msgstr "As credenciais que você forneceu não podem ser determinadas como autênticas." | ||||||
|  |  | ||||||
|  | #: cas_server/forms.py:183 | ||||||
|  | msgid "User not found in the temporary database, please try to reconnect" | ||||||
|  | msgstr "Usuário não encontrado na base de dados temporária, por favor, tente se reconectar" | ||||||
|  |  | ||||||
|  | #: cas_server/forms.py:197 | ||||||
|  | msgid "service" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/management/commands/cas_clean_federate.py:20 | ||||||
|  | msgid "Clean old federated users" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/management/commands/cas_clean_sessions.py:22 | ||||||
|  | msgid "Clean deleted sessions" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/management/commands/cas_clean_tickets.py:22 | ||||||
|  | msgid "Clean old tickets" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:71 | ||||||
|  | msgid "identity provider" | ||||||
|  | msgstr "provedor de identidade" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:72 | ||||||
|  | msgid "identity providers" | ||||||
|  | msgstr "provedores de identidade" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:78 | ||||||
|  | msgid "suffix" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:80 | ||||||
|  | msgid "Suffix append to backend CAS returned username: ``returned_username`` @ ``suffix``." | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:87 | ||||||
|  | msgid "server url" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:97 | ||||||
|  | msgid "CAS protocol version" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:99 | ||||||
|  | msgid "Version of the CAS protocol to use when sending requests the the backend CAS." | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:106 | ||||||
|  | msgid "verbose name" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:107 | ||||||
|  | msgid "Name for this identity provider displayed on the login page." | ||||||
|  | msgstr "Nome para exibir o provedor de identidade na página de login." | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:113 cas_server/models.py:490 | ||||||
|  | msgid "position" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:127 | ||||||
|  | msgid "display" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:128 | ||||||
|  | msgid "Display the provider on the login page." | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:166 | ||||||
|  | msgid "Federated user" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:167 | ||||||
|  | msgid "Federated users" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:246 | ||||||
|  | msgid "User attributes cache" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:247 | ||||||
|  | msgid "User attributes caches" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:271 | ||||||
|  | msgid "User" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:272 | ||||||
|  | msgid "Users" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:364 | ||||||
|  | #, python-format | ||||||
|  | msgid "Error during service logout %s" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:484 | ||||||
|  | msgid "Service pattern" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:485 | ||||||
|  | msgid "Services patterns" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:491 | ||||||
|  | msgid "service patterns are sorted using the position attribute" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:499 cas_server/models.py:664 | ||||||
|  | msgid "name" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:500 | ||||||
|  | msgid "A name for the service" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:508 cas_server/models.py:707 cas_server/models.py:737 | ||||||
|  | msgid "pattern" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:510 | ||||||
|  | msgid "" | ||||||
|  | "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 " | ||||||
|  | "'\\'." | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:521 | ||||||
|  | msgid "user field" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:522 | ||||||
|  | msgid "Name of the attribute to transmit as username, empty = login" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:527 | ||||||
|  | msgid "restrict username" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:528 | ||||||
|  | msgid "Limit username allowed to connect to the list provided bellow" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:533 | ||||||
|  | msgid "proxy" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:534 | ||||||
|  | msgid "Proxy tickets can be delivered to the service" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:540 | ||||||
|  | msgid "proxy callback" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:541 | ||||||
|  | msgid "can be used as a proxy callback to deliver PGT" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:548 | ||||||
|  | msgid "single log out" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:549 | ||||||
|  | msgid "Enable SLO for the service" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:558 | ||||||
|  | msgid "" | ||||||
|  | "URL where the SLO request will be POST. empty = service url\n" | ||||||
|  | "This is usefull for non HTTP proxied services." | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:639 | ||||||
|  | msgid "username allowed to connect to the service" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:665 | ||||||
|  | msgid "name of an attribute to send to the service, use * for all attributes" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:672 cas_server/models.py:745 | ||||||
|  | msgid "replace" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:673 | ||||||
|  | msgid "" | ||||||
|  | "name under which the attribute will be show to the service. empty = default name of the " | ||||||
|  | "attribut" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:700 cas_server/models.py:731 | ||||||
|  | msgid "attribute" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:701 | ||||||
|  | msgid "Name of the attribute which must verify pattern" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:708 | ||||||
|  | msgid "a regular expression" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:732 | ||||||
|  | msgid "Name of the attribute for which the value must be replace" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:738 | ||||||
|  | msgid "An regular expression maching whats need to be replaced" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/models.py:746 | ||||||
|  | msgid "replace expression, groups are capture by \\1, \\2 …" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/templates/cas_server/base.html:43 | ||||||
|  | #, python-format | ||||||
|  | msgid "" | ||||||
|  | "A new version of the application is available. This instance runs %(VERSION)s and the " | ||||||
|  | "last version is %(LAST_VERSION)s. Please consider upgrading." | ||||||
|  | msgstr "" | ||||||
|  | "Uma nova versão da aplicação está disponível. Está instância usa a versão %(VERSION)s e " | ||||||
|  | "a última versão é %(LAST_VERSION)s. Por favor, considere a atualização." | ||||||
|  |  | ||||||
|  | #: cas_server/templates/cas_server/logged.html:4 | ||||||
|  | msgid "" | ||||||
|  | "<h3>Log In Successful</h3>You have successfully logged into the Central Authentication " | ||||||
|  | "Service.<br/>For security reasons, please Log Out and Exit your web browser when you are " | ||||||
|  | "done accessing services that require authentication!" | ||||||
|  | msgstr "" | ||||||
|  | "<h3>Log In realizado com sucesso</h3>Você foi conectado com sucesso a Central de " | ||||||
|  | "Autenticação de Serviços.<br/>Por razões de segurança, faça o Log Out e saia do seu " | ||||||
|  | "navegador quando você terminar de acessar os serviços que exigem auntenticação!" | ||||||
|  |  | ||||||
|  | #: cas_server/templates/cas_server/logged.html:8 | ||||||
|  | msgid "Log me out from all my sessions" | ||||||
|  | msgstr "Desconecte-me de todas as sessões" | ||||||
|  |  | ||||||
|  | #: cas_server/templates/cas_server/logged.html:14 | ||||||
|  | msgid "Forget the identity provider" | ||||||
|  | msgstr "Esquecer o provedor de identidade" | ||||||
|  |  | ||||||
|  | #: cas_server/templates/cas_server/logged.html:18 | ||||||
|  | msgid "Logout" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/templates/cas_server/login.html:6 | ||||||
|  | msgid "Please log in" | ||||||
|  | msgstr "Por favor, faça log in" | ||||||
|  |  | ||||||
|  | #: cas_server/templates/cas_server/login.html:14 | ||||||
|  | msgid "Login" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/templates/cas_server/warn.html:9 | ||||||
|  | msgid "Connect to the service" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/utils.py:744 | ||||||
|  | #, python-format | ||||||
|  | msgid "\"%(value)s\" is not a valid regular expression" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: cas_server/views.py:185 | ||||||
|  | msgid "" | ||||||
|  | "<h3>Logout successful</h3>You have successfully logged out from the Central " | ||||||
|  | "Authentication Service. For security reasons, close your web browser." | ||||||
|  | msgstr "" | ||||||
|  | "<h3>Logout realizado com sucesso</h3>Você foi desconectado com sucesso da Central de " | ||||||
|  | "Autenticação de Serviços. Por razões de segurança, feche seu navegador." | ||||||
|  |  | ||||||
|  | #: cas_server/views.py:191 | ||||||
|  | #, python-format | ||||||
|  | msgid "" | ||||||
|  | "<h3>Logout successful</h3>You have successfully logged out from %s sessions of the " | ||||||
|  | "Central Authentication Service. For security reasons, close your web browser." | ||||||
|  | msgstr "" | ||||||
|  | "<h3>Logout realizado com sucesso</h3>Você foi desconectado com sucesso da %s sessão da " | ||||||
|  | "Centralde Autenticação de Serviços. Por razões de segurança, feche seu navegador." | ||||||
|  |  | ||||||
|  | #: cas_server/views.py:198 | ||||||
|  | msgid "" | ||||||
|  | "<h3>Logout successful</h3>You were already logged out from the Central Authentication " | ||||||
|  | "Service. For security reasons, close your web browser." | ||||||
|  | msgstr "" | ||||||
|  | "<h3>Logout realizado com sucesso</h3>Você já está desconectado da Central de " | ||||||
|  | "Autenticação de Serviços. Por razões de segurança, feche seu navegador." | ||||||
|  |  | ||||||
|  | #: cas_server/views.py:378 | ||||||
|  | #, python-format | ||||||
|  | msgid "" | ||||||
|  | "Invalid response from your identity provider CAS upon ticket %(ticket)s validation: " | ||||||
|  | "%(error)r" | ||||||
|  | msgstr "" | ||||||
|  | "Resposta inválida do provedor de identidade CAS sobre o ticket %(ticket)svalidação: " | ||||||
|  | "%(error)r" | ||||||
|  |  | ||||||
|  | #: cas_server/views.py:500 | ||||||
|  | msgid "Invalid login ticket, please try to log in again" | ||||||
|  | msgstr "Ticket de login inválido, por favor tente novamente" | ||||||
|  |  | ||||||
|  | #: cas_server/views.py:693 | ||||||
|  | #, python-format | ||||||
|  | msgid "Authentication has been required by service %(name)s (%(url)s)" | ||||||
|  | msgstr "Autenticação requerida pelo serviço %(name)s (%(url)s)" | ||||||
|  |  | ||||||
|  | #: cas_server/views.py:731 | ||||||
|  | #, python-format | ||||||
|  | msgid "Service %(url)s not allowed." | ||||||
|  | msgstr "Serviço %(url)s não permitido" | ||||||
|  |  | ||||||
|  | #: cas_server/views.py:738 | ||||||
|  | msgid "Username not allowed" | ||||||
|  | msgstr "Usuário não permitido" | ||||||
|  |  | ||||||
|  | #: cas_server/views.py:745 | ||||||
|  | msgid "User characteristics not allowed" | ||||||
|  | msgstr "Características de usuário não permitida" | ||||||
|  |  | ||||||
|  | #: cas_server/views.py:752 | ||||||
|  | #, python-format | ||||||
|  | msgid "The attribute %(field)s is needed to use that service" | ||||||
|  | msgstr "O atributo %(field)s é necessário para usar o serviço" | ||||||
|  |  | ||||||
|  | #: cas_server/views.py:842 | ||||||
|  | #, python-format | ||||||
|  | msgid "Authentication renewal required by service %(name)s (%(url)s)." | ||||||
|  | msgstr "Renovação da autenticação requerida pelo serviço %(name)s (%(url)s)." | ||||||
|  |  | ||||||
|  | #: cas_server/views.py:849 | ||||||
|  | #, python-format | ||||||
|  | msgid "Authentication required by service %(name)s (%(url)s)." | ||||||
|  | msgstr "Autenticação requerida pelo serviço %(name)s (%(url)s)." | ||||||
|  |  | ||||||
|  | #: cas_server/views.py:856 | ||||||
|  | #, python-format | ||||||
|  | msgid "Service %s not allowed" | ||||||
|  | msgstr "Serviço %s não permitido" | ||||||
							
								
								
									
										30
									
								
								cas_server/migrations/0012_auto_20170328_1610.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								cas_server/migrations/0012_auto_20170328_1610.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Generated by Django 1.10.5 on 2017-03-28 14:10 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ('cas_server', '0011_auto_20161007_1258'), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='federatediendityprovider', | ||||||
|  |             name='cas_protocol_version', | ||||||
|  |             field=models.CharField(choices=[('1', 'CAS 1.0'), ('2', 'CAS 2.0'), ('3', 'CAS 3.0'), ('CAS_2_SAML_1_0', 'SAML 1.1')], default='3', help_text='Version of the CAS protocol to use when sending requests the the backend CAS.', max_length=30, verbose_name='CAS protocol version'), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='servicepattern', | ||||||
|  |             name='single_log_out_callback', | ||||||
|  |             field=models.CharField(blank=True, default='', help_text='URL where the SLO request will be POST. empty = service url\nThis is usefull for non HTTP proxied services.', max_length=255, verbose_name='single log out callback'), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='servicepattern', | ||||||
|  |             name='user_field', | ||||||
|  |             field=models.CharField(blank=True, default='', help_text='Name of the attribute to transmit as username, empty = login', max_length=255, verbose_name='user field'), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										20
									
								
								cas_server/migrations/0013_auto_20170329_1748.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								cas_server/migrations/0013_auto_20170329_1748.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Generated by Django 1.10.5 on 2017-03-29 15:48 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ('cas_server', '0012_auto_20170328_1610'), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name='user', | ||||||
|  |             name='username', | ||||||
|  |             field=models.CharField(max_length=250), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
| @@ -273,7 +273,7 @@ class User(models.Model): | |||||||
|     #: The session key of the current authenticated user |     #: The session key of the current authenticated user | ||||||
|     session_key = models.CharField(max_length=40, blank=True, null=True) |     session_key = models.CharField(max_length=40, blank=True, null=True) | ||||||
|     #: The username of the current authenticated user |     #: The username of the current authenticated user | ||||||
|     username = models.CharField(max_length=30) |     username = models.CharField(max_length=250) | ||||||
|     #: 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 time the user logged | ||||||
|   | |||||||
| @@ -58,7 +58,7 @@ | |||||||
|                         class="alert alert-danger" |                         class="alert alert-danger" | ||||||
|                     {% endif %} |                     {% endif %} | ||||||
|                 {% endspaceless %}> |                 {% endspaceless %}> | ||||||
|                     <p>{{message|safe}}</p> |                     <p>{{message}}</p> | ||||||
|                 </div> |                 </div> | ||||||
|             {% endfor %} |             {% endfor %} | ||||||
|             {% if auto_submit %}</noscript>{% endif %} |             {% if auto_submit %}</noscript>{% endif %} | ||||||
|   | |||||||
| @@ -2,6 +2,6 @@ | |||||||
| {% load staticfiles %} | {% load staticfiles %} | ||||||
| {% load i18n %} | {% load i18n %} | ||||||
| {% block content %} | {% block content %} | ||||||
| <div class="alert alert-success" role="alert">{{logout_msg|safe}}</div> | <div class="alert alert-success" role="alert">{{logout_msg}}</div> | ||||||
| {% endblock %} | {% endblock %} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -295,6 +295,24 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin): | |||||||
|             ) in response.content |             ) in response.content | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |     @override_settings(CAS_SHOW_SERVICE_MESSAGES=False) | ||||||
|  |     def test_view_login_get_allowed_service_no_message(self): | ||||||
|  |         """Request a ticket for an allowed service by an unauthenticated client""" | ||||||
|  |         # get a bare new http client | ||||||
|  |         client = Client() | ||||||
|  |         # we are not authenticated and are asking for a ticket for https://www.example.com | ||||||
|  |         # which is a valid service matched by self.service_pattern | ||||||
|  |         response = client.get("/login?service=https://www.example.com") | ||||||
|  |         # the login page should be displayed | ||||||
|  |         self.assertEqual(response.status_code, 200) | ||||||
|  |         # we warn the user why it need to authenticated | ||||||
|  |         self.assertFalse( | ||||||
|  |             ( | ||||||
|  |                 b"Authentication required by service " | ||||||
|  |                 b"example (https://www.example.com)" | ||||||
|  |             ) in response.content | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     def test_view_login_get_denied_service(self): |     def test_view_login_get_denied_service(self): | ||||||
|         """Request a ticket for an denied service by an unauthenticated client""" |         """Request a ticket for an denied service by an unauthenticated client""" | ||||||
|         # get a bare new http client |         # get a bare new http client | ||||||
| @@ -306,6 +324,18 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin): | |||||||
|         # we warn the user that https://www.example.net is not an allowed service url |         # we warn the user that https://www.example.net is not an allowed service url | ||||||
|         self.assertTrue(b"Service https://www.example.net not allowed" in response.content) |         self.assertTrue(b"Service https://www.example.net not allowed" in response.content) | ||||||
|  |  | ||||||
|  |     @override_settings(CAS_SHOW_SERVICE_MESSAGES=False) | ||||||
|  |     def test_view_login_get_denied_service_no_message(self): | ||||||
|  |         """Request a ticket for an denied service by an unauthenticated client""" | ||||||
|  |         # get a bare new http client | ||||||
|  |         client = Client() | ||||||
|  |         # we are not authenticated and are asking for a ticket for https://www.example.net | ||||||
|  |         # which is NOT a valid service | ||||||
|  |         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.assertFalse(b"Service https://www.example.net not allowed" in response.content) | ||||||
|  |  | ||||||
|     def test_view_login_get_auth_allowed_service(self): |     def test_view_login_get_auth_allowed_service(self): | ||||||
|         """Request a ticket for an allowed service by an authenticated client""" |         """Request a ticket for an allowed service by an authenticated client""" | ||||||
|         # get a client that is already authenticated |         # get a client that is already authenticated | ||||||
| @@ -505,6 +535,40 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin): | |||||||
|         # renewing authentication is done in the validate and serviceValidate views tests |         # renewing authentication is done in the validate and serviceValidate views tests | ||||||
|         self.assertEqual(ticket.renew, True) |         self.assertEqual(ticket.renew, True) | ||||||
|  |  | ||||||
|  |     @override_settings(CAS_SHOW_SERVICE_MESSAGES=False) | ||||||
|  |     def test_renew_message_disabled(self): | ||||||
|  |         """test the authentication renewal request from a service""" | ||||||
|  |         # use the default test service | ||||||
|  |         service = "https://www.example.com" | ||||||
|  |         # get a client that is already authenticated | ||||||
|  |         client = get_auth_client() | ||||||
|  |         # ask for a ticket for the service but aks for authentication renewal | ||||||
|  |         response = client.get("/login", {'service': service, 'renew': 'on'}) | ||||||
|  |         # we are ask to reauthenticate and tell the user why | ||||||
|  |         self.assertEqual(response.status_code, 200) | ||||||
|  |         self.assertFalse( | ||||||
|  |             ( | ||||||
|  |                 b"Authentication renewal required by " | ||||||
|  |                 b"service example (https://www.example.com)" | ||||||
|  |             ) in response.content | ||||||
|  |         ) | ||||||
|  |         # get the form default parameter | ||||||
|  |         params = copy_form(response.context["form"]) | ||||||
|  |         # set valid username/password | ||||||
|  |         params["username"] = settings.CAS_TEST_USER | ||||||
|  |         params["password"] = settings.CAS_TEST_PASSWORD | ||||||
|  |         # the renew parameter from the form should be True | ||||||
|  |         self.assertEqual(params["renew"], True) | ||||||
|  |         # post the authentication request | ||||||
|  |         response = client.post("/login", params) | ||||||
|  |         # the request succed, a ticket is created and we are redirected to the service url | ||||||
|  |         self.assertEqual(response.status_code, 302) | ||||||
|  |         ticket_value = response['Location'].split('ticket=')[-1] | ||||||
|  |         ticket = models.ServiceTicket.objects.get(value=ticket_value) | ||||||
|  |         # the created ticket is marked has being gottent after a renew. Futher testing about | ||||||
|  |         # renewing authentication is done in the validate and serviceValidate views tests | ||||||
|  |         self.assertEqual(ticket.renew, True) | ||||||
|  |  | ||||||
|     @override_settings(CAS_ENABLE_AJAX_AUTH=True) |     @override_settings(CAS_ENABLE_AJAX_AUTH=True) | ||||||
|     def test_ajax_login_required(self): |     def test_ajax_login_required(self): | ||||||
|         """ |         """ | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ from django.views.decorators.csrf import csrf_exempt | |||||||
| from django.middleware.csrf import CsrfViewMiddleware | from django.middleware.csrf import CsrfViewMiddleware | ||||||
| from django.views.generic import View | from django.views.generic import View | ||||||
| from django.utils.encoding import python_2_unicode_compatible | from django.utils.encoding import python_2_unicode_compatible | ||||||
|  | from django.utils.safestring import mark_safe | ||||||
|  |  | ||||||
| import re | import re | ||||||
| import logging | import logging | ||||||
| @@ -181,24 +182,24 @@ class LogoutView(View, LogoutMixin): | |||||||
|         else: |         else: | ||||||
|             # build logout message depending of the number of sessions the user logs out |             # build logout message depending of the number of sessions the user logs out | ||||||
|             if session_nb == 1: |             if session_nb == 1: | ||||||
|                 logout_msg = _( |                 logout_msg = mark_safe(_( | ||||||
|                     "<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, close your web browser." |                     "For security reasons, close your web browser." | ||||||
|                 ) |                 )) | ||||||
|             elif session_nb > 1: |             elif session_nb > 1: | ||||||
|                 logout_msg = _( |                 logout_msg = mark_safe(_( | ||||||
|                     "<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 %d sessions of the Central " | ||||||
|                     "Authentication Service. " |                     "Authentication Service. " | ||||||
|                     "For security reasons, close your web browser." |                     "For security reasons, close your web browser." | ||||||
|                 ) % session_nb |                 ) % session_nb) | ||||||
|             else: |             else: | ||||||
|                 logout_msg = _( |                 logout_msg = mark_safe(_( | ||||||
|                     "<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, close 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 | ||||||
|             # the logout page. The default is to display tge logout page. |             # the logout page. The default is to display tge logout page. | ||||||
| @@ -835,26 +836,29 @@ class LoginView(View, LogoutMixin): | |||||||
|                     # clean messages before leaving django |                     # clean messages before leaving django | ||||||
|                     list(messages.get_messages(self.request)) |                     list(messages.get_messages(self.request)) | ||||||
|                     return HttpResponseRedirect(self.service) |                     return HttpResponseRedirect(self.service) | ||||||
|                 if self.request.session.get("authenticated") and self.renew: |  | ||||||
|                     messages.add_message( |                 if settings.CAS_SHOW_SERVICE_MESSAGES: | ||||||
|                         self.request, |                     if self.request.session.get("authenticated") and self.renew: | ||||||
|                         messages.WARNING, |                         messages.add_message( | ||||||
|                         _(u"Authentication renewal required by service %(name)s (%(url)s).") % |                             self.request, | ||||||
|                         {'name': service_pattern.name, 'url': self.service} |                             messages.WARNING, | ||||||
|                     ) |                             _(u"Authentication renewal required by service %(name)s (%(url)s).") % | ||||||
|                 else: |                             {'name': service_pattern.name, 'url': self.service} | ||||||
|                     messages.add_message( |                         ) | ||||||
|                         self.request, |                     else: | ||||||
|                         messages.WARNING, |                         messages.add_message( | ||||||
|                         _(u"Authentication required by service %(name)s (%(url)s).") % |                             self.request, | ||||||
|                         {'name': service_pattern.name, 'url': self.service} |                             messages.WARNING, | ||||||
|                     ) |                             _(u"Authentication required by service %(name)s (%(url)s).") % | ||||||
|  |                             {'name': service_pattern.name, 'url': self.service} | ||||||
|  |                         ) | ||||||
|             except ServicePattern.DoesNotExist: |             except ServicePattern.DoesNotExist: | ||||||
|                 messages.add_message( |                 if settings.CAS_SHOW_SERVICE_MESSAGES: | ||||||
|                     self.request, |                     messages.add_message( | ||||||
|                     messages.ERROR, |                         self.request, | ||||||
|                     _(u'Service %s not allowed') % self.service |                         messages.ERROR, | ||||||
|                 ) |                         _(u'Service %s not allowed') % self.service | ||||||
|  |                     ) | ||||||
|         if self.ajax: |         if self.ajax: | ||||||
|             data = { |             data = { | ||||||
|                 "status": "error", |                 "status": "error", | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user