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

Merge pull request #9 from nitmir/dev

Update to version 0.6.2
parents 8b27b8c5 773707e6
language: python language: python
matrix: matrix:
include: include:
- python: "2.7"
env: TOX_ENV=coverage
- python: "2.7" - python: "2.7"
env: TOX_ENV=flake8 env: TOX_ENV=flake8
- python: "2.7" - python: "2.7"
...@@ -23,6 +21,8 @@ matrix: ...@@ -23,6 +21,8 @@ matrix:
env: TOX_ENV=py35-django18 env: TOX_ENV=py35-django18
- python: "3.5" - python: "3.5"
env: TOX_ENV=py35-django19 env: TOX_ENV=py35-django19
- python: "2.7"
env: TOX_ENV=coverage
cache: cache:
directories: directories:
- $HOME/.cache/pip/http/ - $HOME/.cache/pip/http/
......
...@@ -38,7 +38,7 @@ dist: ...@@ -38,7 +38,7 @@ dist:
test_venv/bin/python: test_venv/bin/python:
virtualenv test_venv virtualenv test_venv
test_venv/bin/pip install -U --requirement requirements-dev.txt Django test_venv/bin/pip install -U --requirement requirements-dev.txt 'Django<1.10'
test_venv/cas/manage.py: test_venv test_venv/cas/manage.py: test_venv
mkdir -p test_venv/cas mkdir -p test_venv/cas
...@@ -62,7 +62,7 @@ run_server: test_project ...@@ -62,7 +62,7 @@ run_server: test_project
run_tests: test_venv run_tests: test_venv
python setup.py check --restructuredtext --stric python setup.py check --restructuredtext --stric
test_venv/bin/py.test --cov=cas_server --cov-report html test_venv/bin/py.test -rw -x --cov=cas_server --cov-report html
rm htmlcov/coverage_html.js # I am really pissed off by those keybord shortcuts rm htmlcov/coverage_html.js # I am really pissed off by those keybord shortcuts
test_venv/bin/sphinx-build: test_venv test_venv/bin/sphinx-build: test_venv
......
This diff is collapsed.
...@@ -9,5 +9,9 @@ ...@@ -9,5 +9,9 @@
# #
# (c) 2015-2016 Valentin Samir # (c) 2015-2016 Valentin Samir
"""A django CAS server application""" """A django CAS server application"""
#: version of the application
VERSION = '0.6.2'
#: 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'
...@@ -13,16 +13,25 @@ ...@@ -13,16 +13,25 @@
from django.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.utils import timezone from django.utils import timezone
from django.db import connections, DatabaseError
import warnings
from datetime import timedelta from datetime import timedelta
from six.moves import range
try: # pragma: no cover try: # pragma: no cover
import MySQLdb import MySQLdb
import MySQLdb.cursors import MySQLdb.cursors
from utils import check_password
except ImportError: except ImportError:
MySQLdb = None MySQLdb = None
try: # pragma: no cover
import ldap3
except ImportError:
ldap3 = None
from .models import FederatedUser from .models import FederatedUser
from .utils import check_password, dictfetchall
class AuthUser(object): class AuthUser(object):
...@@ -116,19 +125,46 @@ class TestAuthUser(AuthUser): ...@@ -116,19 +125,46 @@ class TestAuthUser(AuthUser):
return {} return {}
class MysqlAuthUser(AuthUser): # pragma: no cover class DBAuthUser(AuthUser): # pragma: no cover
"""base class for databate based auth classes"""
#: DB user attributes as a :class:`dict` if the username is found in the database.
user = None
def attributs(self):
"""
The user attributes.
:return: a :class:`dict` with the user attributes. Attributes may be :func:`unicode`
or :class:`list` of :func:`unicode`. If the user do not exists, the returned
:class:`dict` is empty.
:rtype: dict
"""
if self.user:
return self.user
else:
return {}
class MysqlAuthUser(DBAuthUser): # pragma: no cover
""" """
A mysql authentication class: authentication user agains a mysql database DEPRECATED, use :class:`SqlAuthUser` instead.
A mysql authentication class: authenticate user agains a mysql database
:param unicode username: A username, stored in the :attr:`username<AuthUser.username>` :param unicode username: A username, stored in the :attr:`username<AuthUser.username>`
class attribute. Valid value are fetched from the MySQL database set with class attribute. Valid value are fetched from the MySQL database set with
``settings.CAS_SQL_*`` settings parameters using the query ``settings.CAS_SQL_*`` settings parameters using the query
``settings.CAS_SQL_USER_QUERY``. ``settings.CAS_SQL_USER_QUERY``.
""" """
#: Mysql user attributes as a :class:`dict` if the username is found in the database.
user = None
def __init__(self, username): def __init__(self, username):
warnings.warn(
(
"MysqlAuthUser authentication class is deprecated: "
"use cas_server.auth.SqlAuthUser instead"
),
UserWarning
)
# see the connect function at # see the connect function at
# http://mysql-python.sourceforge.net/MySQLdb.html#functions-and-attributes # http://mysql-python.sourceforge.net/MySQLdb.html#functions-and-attributes
# for possible mysql config parameters. # for possible mysql config parameters.
...@@ -169,24 +205,130 @@ class MysqlAuthUser(AuthUser): # pragma: no cover ...@@ -169,24 +205,130 @@ class MysqlAuthUser(AuthUser): # pragma: no cover
else: else:
return False return False
def attributs(self):
class SqlAuthUser(DBAuthUser): # pragma: no cover
"""
A SQL authentication class: authenticate user agains a SQL database. The SQL database
must be configures in settings.py as ``settings.DATABASES['cas_server']``.
:param unicode username: A username, stored in the :attr:`username<AuthUser.username>`
class attribute. Valid value are fetched from the MySQL database set with
``settings.CAS_SQL_*`` settings parameters using the query
``settings.CAS_SQL_USER_QUERY``.
"""
def __init__(self, username):
if "cas_server" not in connections:
raise RuntimeError("Please configure the 'cas_server' database in settings.DATABASES")
for retry_nb in range(3):
try:
with connections["cas_server"].cursor() as curs:
curs.execute(settings.CAS_SQL_USER_QUERY, (username,))
results = dictfetchall(curs)
if len(results) == 1:
self.user = results[0]
super(SqlAuthUser, self).__init__(self.user['username'])
else:
super(SqlAuthUser, self).__init__(username)
break
except DatabaseError:
connections["cas_server"].close()
if retry_nb == 2:
raise
def test_password(self, password):
""" """
The user attributes. Tests ``password`` agains the user password.
:return: a :class:`dict` with the user attributes. Attributes may be :func:`unicode` :param unicode password: a clear text password as submited by the user.
or :class:`list` of :func:`unicode`. If the user do not exists, the returned :return: ``True`` if :attr:`username<AuthUser.username>` is valid and ``password`` is
:class:`dict` is empty. correct, ``False`` otherwise.
:rtype: dict :rtype: bool
""" """
if self.user: if self.user:
return self.user return check_password(
settings.CAS_SQL_PASSWORD_CHECK,
password,
self.user["password"],
settings.CAS_SQL_PASSWORD_CHARSET
)
else: else:
return {} return False
class LdapAuthUser(DBAuthUser): # pragma: no cover
"""
A ldap authentication class: authenticate user against a ldap database
:param unicode username: A username, stored in the :attr:`username<AuthUser.username>`
class attribute. Valid value are fetched from the ldap database set with
``settings.CAS_LDAP_*`` settings parameters.
"""
_conn = None
@classmethod
def get_conn(cls):
"""Return a connection object to the ldap database"""
conn = cls._conn
if conn is None or conn.closed:
conn = ldap3.Connection(
settings.CAS_LDAP_SERVER,
settings.CAS_LDAP_USER,
settings.CAS_LDAP_PASSWORD,
auto_bind=True
)
cls._conn = conn
return conn
def __init__(self, username):
if not ldap3:
raise RuntimeError("Please install ldap3 before using the LdapAuthUser backend")
# in case we got deconnected from the database, retry to connect 2 times
for retry_nb in range(3):
try:
conn = self.get_conn()
if conn.search(
settings.CAS_LDAP_BASE_DN,
settings.CAS_LDAP_USER_QUERY % ldap3.utils.conv.escape_bytes(username),
attributes=ldap3.ALL_ATTRIBUTES
) and len(conn.entries) == 1:
user = conn.entries[0].entry_get_attributes_dict()
if user.get(settings.CAS_LDAP_USERNAME_ATTR):
self.user = user
super(LdapAuthUser, self).__init__(user[settings.CAS_LDAP_USERNAME_ATTR][0])
else:
super(LdapAuthUser, self).__init__(username)
else:
super(LdapAuthUser, self).__init__(username)
break
except ldap3.LDAPCommunicationError:
if retry_nb == 2:
raise
def test_password(self, password):
"""
Tests ``password`` agains the user password.
:param unicode password: a clear text password as submited by the user.
:return: ``True`` if :attr:`username<AuthUser.username>` is valid and ``password`` is
correct, ``False`` otherwise.
:rtype: bool
"""
if self.user and self.user.get(settings.CAS_LDAP_PASSWORD_ATTR):
return check_password(
settings.CAS_LDAP_PASSWORD_CHECK,
password,
self.user[settings.CAS_LDAP_PASSWORD_ATTR][0],
settings.CAS_LDAP_PASSWORD_CHARSET
)
else:
return False
class DjangoAuthUser(AuthUser): # pragma: no cover class DjangoAuthUser(AuthUser): # pragma: no cover
""" """
A django auth class: authenticate user agains django internal users A django auth class: authenticate user against django internal users
:param unicode username: A username, stored in the :attr:`username<AuthUser.username>` :param unicode username: A username, stored in the :attr:`username<AuthUser.username>`
class attribute. Valid value are usernames of django internal users. class attribute. Valid value are usernames of django internal users.
......
...@@ -134,6 +134,14 @@ class CASClientBase(object): ...@@ -134,6 +134,14 @@ class CASClientBase(object):
raise CASError(errors[0].attrib['code'], errors[0].text) raise CASError(errors[0].attrib['code'], errors[0].text)
raise CASError("Bad http code %s" % response.code) raise CASError("Bad http code %s" % response.code)
@staticmethod
def get_page_charset(page, default="utf-8"):
content_type = page.info().get('Content-type')
if content_type and "charset=" in content_type:
return content_type.split("charset=")[-1]
else:
return default
class CASClientV1(CASClientBase, ReturnUnicode): class CASClientV1(CASClientBase, ReturnUnicode):
"""CAS Client Version 1""" """CAS Client Version 1"""
...@@ -146,17 +154,15 @@ class CASClientV1(CASClientBase, ReturnUnicode): ...@@ -146,17 +154,15 @@ class CASClientV1(CASClientBase, ReturnUnicode):
Returns username on success and None on failure. Returns username on success and None on failure.
""" """
params = [('ticket', ticket), ('service', self.service_url)] params = [('ticket', ticket), ('service', self.service_url)]
if self.renew:
params.append(('renew', 'true'))
url = (urllib_parse.urljoin(self.server_url, 'validate') + '?' + url = (urllib_parse.urljoin(self.server_url, 'validate') + '?' +
urllib_parse.urlencode(params)) urllib_parse.urlencode(params))
page = urllib_request.urlopen(url) page = urllib_request.urlopen(url)
try: try:
verified = page.readline().strip() verified = page.readline().strip()
if verified == b'yes': if verified == b'yes':
content_type = page.info().get('Content-type') charset = self.get_page_charset(page, default="ascii")
if "charset=" in content_type:
charset = content_type.split("charset=")[-1]
else:
charset = "ascii"
user = self.u(page.readline().strip(), charset) user = self.u(page.readline().strip(), charset)
return user, None, None return user, None, None
else: else:
...@@ -183,17 +189,15 @@ class CASClientV2(CASClientBase, ReturnUnicode): ...@@ -183,17 +189,15 @@ class CASClientV2(CASClientBase, ReturnUnicode):
def get_verification_response(self, ticket): def get_verification_response(self, ticket):
params = [('ticket', ticket), ('service', self.service_url)] params = [('ticket', ticket), ('service', self.service_url)]
if self.renew:
params.append(('renew', 'true'))
if self.proxy_callback: if self.proxy_callback:
params.append(('pgtUrl', self.proxy_callback)) params.append(('pgtUrl', self.proxy_callback))
base_url = urllib_parse.urljoin(self.server_url, self.url_suffix) base_url = urllib_parse.urljoin(self.server_url, self.url_suffix)
url = base_url + '?' + urllib_parse.urlencode(params) url = base_url + '?' + urllib_parse.urlencode(params)
page = urllib_request.urlopen(url) page = urllib_request.urlopen(url)
try: try:
content_type = page.info().get('Content-type') charset = self.get_page_charset(page)
if "charset=" in content_type:
charset = content_type.split("charset=")[-1]
else:
charset = "ascii"
return (page.read(), charset) return (page.read(), charset)
finally: finally:
page.close() page.close()
...@@ -306,11 +310,7 @@ class CASClientWithSAMLV1(CASClientV2, SingleLogoutMixin): ...@@ -306,11 +310,7 @@ class CASClientWithSAMLV1(CASClientV2, SingleLogoutMixin):
from elementtree import ElementTree from elementtree import ElementTree
page = self.fetch_saml_validation(ticket) page = self.fetch_saml_validation(ticket)
content_type = page.info().get('Content-type') charset = self.get_page_charset(page)
if "charset=" in content_type:
charset = content_type.split("charset=")[-1]
else:
charset = "ascii"
try: try:
user = None user = None
......
...@@ -18,6 +18,8 @@ from importlib import import_module ...@@ -18,6 +18,8 @@ from importlib import import_module
#: URL to the logo showed in the up left corner on the default templates. #: URL to the logo showed in the up left corner on the default templates.
CAS_LOGO_URL = static("cas_server/logo.png") CAS_LOGO_URL = static("cas_server/logo.png")
#: URL to the favicon (shortcut icon) used by the default templates. Default is a key icon.
CAS_FAVICON_URL = static("cas_server/favicon.ico")
#: URLs to css and javascript external components. #: URLs to css and javascript external components.
CAS_COMPONENT_URLS = { CAS_COMPONENT_URLS = {
"bootstrap3_css": "//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css", "bootstrap3_css": "//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css",
...@@ -110,12 +112,39 @@ CAS_SQL_PASSWORD = '' ...@@ -110,12 +112,39 @@ CAS_SQL_PASSWORD = ''
CAS_SQL_DBNAME = '' CAS_SQL_DBNAME = ''
#: Database charset. #: Database charset.
CAS_SQL_DBCHARSET = 'utf8' CAS_SQL_DBCHARSET = 'utf8'
#: The query performed upon user authentication. #: The query performed upon user authentication.
CAS_SQL_USER_QUERY = 'SELECT user AS usersame, pass AS password, users.* FROM users WHERE user = %s' CAS_SQL_USER_QUERY = 'SELECT user AS username, pass AS password, users.* FROM users WHERE user = %s'
#: The method used to check the user password. Must be one of ``"crypt"``, ``"ldap"``, #: The method used to check the user password. Must be one of ``"crypt"``, ``"ldap"``,
#: ``"hex_md5"``, ``"hex_sha1"``, ``"hex_sha224"``, ``"hex_sha256"``, ``"hex_sha384"``, #: ``"hex_md5"``, ``"hex_sha1"``, ``"hex_sha224"``, ``"hex_sha256"``, ``"hex_sha384"``,
#: ``"hex_sha512"``, ``"plain"``. #: ``"hex_sha512"``, ``"plain"``.
CAS_SQL_PASSWORD_CHECK = 'crypt' # crypt or plain CAS_SQL_PASSWORD_CHECK = 'crypt'
#: charset the SQL users passwords was hash with
CAS_SQL_PASSWORD_CHARSET = "utf-8"
#: Address of the LDAP server
CAS_LDAP_SERVER = 'localhost'
#: LDAP user bind address, for example ``"cn=admin,dc=crans,dc=org"`` for connecting to the LDAP
#: server.
CAS_LDAP_USER = None
#: LDAP connection password
CAS_LDAP_PASSWORD = None
#: LDAP seach base DN, for example ``"ou=data,dc=crans,dc=org"``.
CAS_LDAP_BASE_DN = None
#: LDAP search filter for searching user by username. User inputed usernames are escaped using
#: :func:`ldap3.utils.conv.escape_bytes`.
CAS_LDAP_USER_QUERY = "(uid=%s)"
#: LDAP attribute used for users usernames
CAS_LDAP_USERNAME_ATTR = "uid"
#: LDAP attribute used for users passwords
CAS_LDAP_PASSWORD_ATTR = "userPassword"
#: The method used to check the user password. Must be one of ``"crypt"``, ``"ldap"``,
#: ``"hex_md5"``, ``"hex_sha1"``, ``"hex_sha224"``, ``"hex_sha256"``, ``"hex_sha384"``,
#: ``"hex_sha512"``, ``"plain"``.
CAS_LDAP_PASSWORD_CHECK = "ldap"
#: charset the LDAP users passwords was hash with
CAS_LDAP_PASSWORD_CHARSET = "utf-8"
#: Username of the test user. #: Username of the test user.
...@@ -140,6 +169,15 @@ CAS_FEDERATE = False ...@@ -140,6 +169,15 @@ CAS_FEDERATE = False
#: Time after witch the cookie use for “remember my identity provider” expire (one week). #: Time after witch the cookie use for “remember my identity provider” expire (one week).
CAS_FEDERATE_REMEMBER_TIMEOUT = 604800 CAS_FEDERATE_REMEMBER_TIMEOUT = 604800
#: A :class:`bool` for diplaying a warning on html pages then a new version of the application
#: is avaible. Once closed by a user, it is not displayed to this user until the next new version.
CAS_NEW_VERSION_HTML_WARNING = True
#: A :class:`bool` for sending emails to ``settings.ADMINS`` when a new version is available.
CAS_NEW_VERSION_EMAIL_WARNING = True
#: URL to the pypi json of the application. Used to retreive the version number of the last version.
#: You should not change it.
CAS_NEW_VERSION_JSON_URL = "https://pypi.python.org/pypi/django-cas-server/json"
GLOBALS = globals().copy() GLOBALS = globals().copy()
for name, default_value in GLOBALS.items(): for name, default_value in GLOBALS.items():
# get the current setting value, falling back to default_value # get the current setting value, falling back to default_value
......
...@@ -42,13 +42,13 @@ class CASFederateValidateUser(object): ...@@ -42,13 +42,13 @@ class CASFederateValidateUser(object):
#: the identity provider #: the identity provider
provider = None provider = None
def __init__(self, provider, service_url): def __init__(self, provider, service_url, renew=False):
self.provider = provider self.provider = provider
self.client = CASClient( self.client = CASClient(
service_url=service_url, service_url=service_url,
version=provider.cas_protocol_version, version=provider.cas_protocol_version,
server_url=provider.server_url, server_url=provider.server_url,
renew=False, renew=renew,
) )
def get_login_url(self): def get_login_url(self):
......
...@@ -19,49 +19,56 @@ import cas_server.models as models ...@@ -19,49 +19,56 @@ import cas_server.models as models
class BootsrapForm(forms.Form): class BootsrapForm(forms.Form):
"""Form base class to use boostrap then rendering the form fields""" """
Bases: :class:`django.forms.Form`
Form base class to use boostrap then rendering the form fields
"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(BootsrapForm, self).__init__(*args, **kwargs) super(BootsrapForm, self).__init__(*args, **kwargs)
for (name, field) in self.fields.items(): for field in self.fields.values():
# Only tweak the fiel if it will be displayed # Only tweak the fiel if it will be displayed
if not isinstance(field.widget, forms.HiddenInput): if not isinstance(field.widget, forms.HiddenInput):
# tell to display the field (used in form.html)
self[name].display = True
attrs = {} attrs = {}
if isinstance(field.widget, forms.CheckboxInput): if not isinstance(field.widget, forms.CheckboxInput):
self[name].checkbox = True
else:
attrs['class'] = "form-control" attrs['class'] = "form-control"
if field.label: if field.label: # pragma: no branch (currently all field are hidden or labeled)
attrs["placeholder"] = field.label attrs["placeholder"] = field.label
if field.required: if field.required:
attrs["required"] = "required" attrs["required"] = "required"
field.widget.attrs.update(attrs) field.widget.attrs.update(attrs)
class WarnForm(BootsrapForm): class BaseLogin(BootsrapForm):
""" """
Bases: :class:`django.forms.Form` Bases: :class:`BootsrapForm`
Form used on warn page before emiting a ticket Base form with all field possibly hidden on the login pages
""" """
#: The service url for which the user want a ticket #: The service url for which the user want a ticket
service = forms.CharField(widget=forms.HiddenInput(), required=False) service = forms.CharField(widget=forms.HiddenInput(), required=False)
#: A valid LoginTicket to prevent POST replay
lt = forms.CharField(widget=forms.HiddenInput(), required=False)
#: Is the service asking the authentication renewal ? #: Is the service asking the authentication renewal ?
renew = forms.BooleanField(widget=forms.HiddenInput(), required=False) renew = forms.BooleanField(widget=forms.HiddenInput(), required=False)
#: Url to redirect to if the authentication fail (user not authenticated or bad service) #: Url to redirect to if the authentication fail (user not authenticated or bad service)
gateway = forms.CharField(widget=forms.HiddenInput(), required=False) gateway = forms.CharField(widget=forms.HiddenInput(), required=False)
method = forms.CharField(widget=forms.HiddenInput(), required=False) method = forms.CharField(widget=forms.HiddenInput(), required=False)
class WarnForm(BaseLogin):
"""
Bases: :class:`BaseLogin`
Form used on warn page before emiting a ticket
"""
#: ``True`` if the user has been warned of the ticket emission #: ``True`` if the user has been warned of the ticket emission
warned = forms.BooleanField(widget=forms.HiddenInput(), required=False) warned = forms.BooleanField(widget=forms.HiddenInput(), required=False)
#: A valid LoginTicket to prevent POST replay
lt = forms.CharField(widget=forms.HiddenInput(), required=False)
class FederateSelect(BootsrapForm): class FederateSelect(BaseLogin):
""" """
Bases: :class:`django.forms.Form` Bases: :class:`BaseLogin`
Form used on the login page when ``settings.CAS_FEDERATE`` is ``True`` Form used on the login page when ``settings.CAS_FEDERATE`` is ``True``
allowing the user to choose an identity provider. allowing the user to choose an identity provider.
...@@ -76,39 +83,30 @@ class FederateSelect(BootsrapForm): ...@@ -76,39 +83,30 @@ class FederateSelect(BootsrapForm):
to_field_name="suffix", to_field_name="suffix",
label=_('Identity provider'), label=_('Identity provider'),
) )
#: The service url for which the user want a ticket #: A checkbox to ask to be warn before emiting a ticket for another service
service = forms.CharField(label=_('service'), widget=forms.HiddenInput(), required=False) warn = forms.BooleanField(
method = forms.CharField(widget=forms.HiddenInput(), required=False) label=_('Warn me before logging me into other sites.'),
required=False
)
#: A checkbox to remember the user choices of :attr:`provider<FederateSelect.provider>` #: A checkbox to remember the user choices of :attr:`provider<FederateSelect.provider>`
remember = forms.BooleanField(label=_('Remember the identity provider'), required=False) remember = forms.BooleanField(label=_('Remember the identity provider'), required=False)
#: A checkbox to ask to be warn before emiting a ticket for another service
warn = forms.BooleanField(label=_('warn'), required=False)
#: Is the service asking the authentication renewal ?
renew = forms.BooleanField(widget=forms.HiddenInput(), required=False)
class UserCredential(BootsrapForm): class UserCredential(BaseLogin):
""" """
Bases: :class:`django.forms.Form` Bases: :class:`BaseLogin`
Form used on the login page to retrive user credentials Form used on the login page to retrive user credentials
""" """
#: The user username #: The user username
username = forms.CharField(label=_('login')) username = forms.CharField(label=_('username'))
#: The service url for which the user want a ticket
service = forms.CharField(label=_('service'), widget=forms.HiddenInput(), required=False)
#: The user password #: The user password
password = forms.CharField(label=_('password'), widget=forms.PasswordInput) password = forms.CharField(label=_('password'), widget=forms.PasswordInput)
#: A valid LoginTicket to prevent POST replay
lt = forms.CharField(widget=forms.HiddenInput(), required=False)
method = forms.CharField(widget=forms.HiddenInput(), required=False)
#: A checkbox to ask to be warn before emiting a ticket for another service #: A checkbox to ask to be warn before emiting a ticket for another service
warn = forms.BooleanField(label=_('warn'), required=False) warn = forms.BooleanField(
#: Is the service asking the authentication renewal ? label=_('Warn me before logging me into other sites.'),
renew = forms.BooleanField(widget=forms.HiddenInput(), required=False) required=False
)
def __init__(self, *args, **kwargs):
super(UserCredential, self).__init__(*args, **kwargs)
def clean(self): def clean(self):
""" """
...@@ -124,7 +122,9 @@ class UserCredential(BootsrapForm): ...@@ -124,7 +122,9 @@ class UserCredential(BootsrapForm):
if auth.test_password(cleaned_data.get("password")): if auth.test_password(cleaned_data.get("password")):
cleaned_data["username"] = auth.username cleaned_data["username"] = auth.username
else: else:
raise forms.ValidationError(_(u"Bad user")) raise forms.ValidationError(
_(u"The credentials you provided cannot be determined to be authentic.")
)
return cleaned_data return cleaned_data
...@@ -148,21 +148,13 @@ class FederateUserCredential(UserCredential): ...@@ -148,21 +148,13 @@ class FederateUserCredential(UserCredential):
This stub authentication form, allow to implement the federated mode with very few This stub authentication form, allow to implement the federated mode with very few
modificatons to the :class:`LoginView<cas_server.views.LoginView>` view. modificatons to the :class:`LoginView<cas_server.views.LoginView>` view.
""" """
#: the user username with the ``@`` component
username = forms.CharField(widget=forms.HiddenInput()) def __init__(self, *args, **kwargs):
#: The service url for which the user want a ticket super(FederateUserCredential, self).__init__(*args, **kwargs)
service = forms.CharField(widget=forms.HiddenInput(), required=False) # All fields are hidden and auto filled by the /login view logic
#: The ``ticket`` used to authenticate the user against a provider for name, field in self.fields.items():
password = forms.CharField(widget=forms.HiddenInput()) field.widget = forms.HiddenInput()
#: alias of :attr:`password` self[name].display = False
ticket = forms.CharField(widget=forms.HiddenInput())
#: A valid LoginTicket to prevent POST replay
lt = forms.CharField(widget=forms.HiddenInput(), required=False)
method = forms.CharField(widget=forms.HiddenInput(), required=False)
#: Has the user asked to be warn before emiting a ticket for another service
warn = forms.BooleanField(widget=forms.HiddenInput(), required=False)
#: Is the service asking the authentication renewal ?
renew = forms.BooleanField(widget=forms.HiddenInput(), required=False)