Use django admin application to add/modif identty providers when CAS_FEDERATE is True
This commit is contained in:
parent
40b4f07001
commit
aa433d3c58
18 changed files with 600 additions and 388 deletions
|
@ -12,6 +12,7 @@ exclude_lines =
|
||||||
pragma: no cover
|
pragma: no cover
|
||||||
def __repr__
|
def __repr__
|
||||||
def __unicode__
|
def __unicode__
|
||||||
|
def __str__
|
||||||
raise AssertionError
|
raise AssertionError
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
if six.PY3:
|
if six.PY3:
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -15,3 +15,5 @@ coverage.xml
|
||||||
test_venv
|
test_venv
|
||||||
.coverage
|
.coverage
|
||||||
htmlcov/
|
htmlcov/
|
||||||
|
tox_logs/
|
||||||
|
.cache/
|
||||||
|
|
39
README.rst
39
README.rst
|
@ -165,12 +165,6 @@ Federation settings
|
||||||
|
|
||||||
* ``CAS_FEDERATE``: A boolean for activating the federated mode (see the federate section below).
|
* ``CAS_FEDERATE``: A boolean for activating the federated mode (see the federate section below).
|
||||||
The default is ``False``.
|
The default is ``False``.
|
||||||
* ``CAS_FEDERATE_PROVIDERS``: A dictionnary for the allowed identity providers (see the federate
|
|
||||||
section below). The default is ``{}``.
|
|
||||||
* ``CAS_FEDERATE_PROVIDERS_LIST``: A list in with the keys of ``CAS_FEDERATE_PROVIDERS`` are ordened
|
|
||||||
for beeing displayed on the login page. The default is the list of all the keys of
|
|
||||||
``CAS_FEDERATE_PROVIDERS`` sorted in natural order (0 < 2 < 10 < 20 < a = A < … < z = Z and
|
|
||||||
lexicographical)
|
|
||||||
* ``CAS_FEDERATE_REMEMBER_TIMEOUT``: Time after witch the cookie use for "remember my identity
|
* ``CAS_FEDERATE_REMEMBER_TIMEOUT``: Time after witch the cookie use for "remember my identity
|
||||||
provider" expire. The default is ``604800``, one week. The cookie is called
|
provider" expire. The default is ``604800``, one week. The cookie is called
|
||||||
``_remember_provider``.
|
``_remember_provider``.
|
||||||
|
@ -344,26 +338,29 @@ to the provider CAS to authenticate. This provider transmit to ``django-cas-serv
|
||||||
username and attributes. The user is now logged in on ``django-cas-server`` and can use
|
username and attributes. The user is now logged in on ``django-cas-server`` and can use
|
||||||
services using ``django-cas-server`` as CAS.
|
services using ``django-cas-server`` as CAS.
|
||||||
|
|
||||||
The list of allowed identity providers is defined using the ``CAS_FEDERATE_PROVIDERS`` parameter.
|
The list of allowed identity providers is defined using the django admin application.
|
||||||
For instance:
|
With the development server started, visit http://127.0.0.1:8000/admin/ to add identity providers.
|
||||||
|
|
||||||
.. code-block:: python
|
An identity provider comes with 5 fields:
|
||||||
|
|
||||||
CAS_FEDERATE_PROVIDERS = {
|
* `Position`: an integer used to tweak the order in which identity providers are displayed on
|
||||||
"example.com": ("https://cas.example.com", 3, "Example dot com"),
|
the login page. Identity providers are sorted using position first, then, on equal position,
|
||||||
"exemple.fr": ("https://cas.exemple.fr", 3, "Exemple point fr"),
|
using `verbose name` and then, on equal `verbose name`, using `suffix`.
|
||||||
}
|
* `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`
|
||||||
|
* `CAS protocol version`: the version of the CAS protocol to use to contact the identity provider.
|
||||||
|
The default is version 3.
|
||||||
|
* `Verbose name`: the name used on the login page to display the identity provider.
|
||||||
|
|
||||||
|
|
||||||
``CAS_FEDERATE_PROVIDERS`` is a dictionnary using provider names as key and a tuple
|
|
||||||
(cas address, cas version protocol, provider verbose name) as value.
|
|
||||||
|
|
||||||
In federation mode, ``django-cas-server`` build user's username as follow:
|
In federation mode, ``django-cas-server`` build user's username as follow:
|
||||||
``provider_returned_username@provider_name``.
|
``provider_returned_username@provider_suffix``.
|
||||||
You can choose the provider returned username for ``django-cas-server`` and the provider name
|
Choose the provider returned username for ``django-cas-server`` and the provider suffix
|
||||||
in order to make sense.
|
in order to make sense, as this built username is likely to be displayed to end users in
|
||||||
|
applications.
|
||||||
The "provider verbose name" is showed on the select menu of the login page.
|
|
||||||
|
|
||||||
|
|
||||||
Then using federate mode, you should add one command to a daily crontab: ``cas_clean_federate``.
|
Then using federate mode, you should add one command to a daily crontab: ``cas_clean_federate``.
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import ServiceTicket, ProxyTicket, ProxyGrantingTicket, User, ServicePattern
|
from .models import ServiceTicket, ProxyTicket, ProxyGrantingTicket, User, ServicePattern
|
||||||
from .models import Username, ReplaceAttributName, ReplaceAttributValue, FilterAttributValue
|
from .models import Username, ReplaceAttributName, ReplaceAttributValue, FilterAttributValue
|
||||||
|
from .models import FederatedIendityProvider
|
||||||
from .forms import TicketForm
|
from .forms import TicketForm
|
||||||
|
|
||||||
TICKETS_READONLY_FIELDS = ('validate', 'service', 'service_pattern',
|
TICKETS_READONLY_FIELDS = ('validate', 'service', 'service_pattern',
|
||||||
|
@ -91,5 +92,10 @@ class ServicePatternAdmin(admin.ModelAdmin):
|
||||||
'single_log_out', 'proxy_callback', 'restrict_users')
|
'single_log_out', 'proxy_callback', 'restrict_users')
|
||||||
|
|
||||||
|
|
||||||
|
class FederatedIendityProviderAdmin(admin.ModelAdmin):
|
||||||
|
fields = ('pos', 'suffix', 'server_url', 'cas_protocol_version', 'verbose_name')
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
admin.site.register(ServicePattern, ServicePatternAdmin)
|
admin.site.register(ServicePattern, ServicePatternAdmin)
|
||||||
|
admin.site.register(FederatedIendityProvider, FederatedIendityProviderAdmin)
|
||||||
|
|
|
@ -148,16 +148,13 @@ class CASFederateAuth(AuthUser):
|
||||||
user = None
|
user = None
|
||||||
|
|
||||||
def __init__(self, username):
|
def __init__(self, username):
|
||||||
component = username.split('@')
|
|
||||||
username = '@'.join(component[:-1])
|
|
||||||
provider = component[-1]
|
|
||||||
try:
|
try:
|
||||||
self.user = FederatedUser.objects.get(username=username, provider=provider)
|
self.user = FederatedUser.get_from_federated_username(username)
|
||||||
super(CASFederateAuth, self).__init__(
|
super(CASFederateAuth, self).__init__(
|
||||||
"%s@%s" % (self.user.username, self.user.provider)
|
self.user.federated_username
|
||||||
)
|
)
|
||||||
except FederatedUser.DoesNotExist:
|
except FederatedUser.DoesNotExist:
|
||||||
super(CASFederateAuth, self).__init__("%s@%s" % (username, provider))
|
super(CASFederateAuth, self).__init__(username)
|
||||||
|
|
||||||
def test_password(self, ticket):
|
def test_password(self, ticket):
|
||||||
"""test `password` agains the user"""
|
"""test `password` agains the user"""
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.staticfiles.templatetags.staticfiles import static
|
from django.contrib.staticfiles.templatetags.staticfiles import static
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
def setting_default(name, default_value):
|
def setting_default(name, default_value):
|
||||||
"""if the config `name` is not set, set it the `default_value`"""
|
"""if the config `name` is not set, set it the `default_value`"""
|
||||||
|
@ -92,30 +90,7 @@ setting_default(
|
||||||
setting_default('CAS_ENABLE_AJAX_AUTH', False)
|
setting_default('CAS_ENABLE_AJAX_AUTH', False)
|
||||||
|
|
||||||
setting_default('CAS_FEDERATE', False)
|
setting_default('CAS_FEDERATE', False)
|
||||||
# A dict of "provider suffix" -> (provider CAS server url, CAS version, verbose name)
|
|
||||||
setting_default('CAS_FEDERATE_PROVIDERS', {})
|
|
||||||
setting_default('CAS_FEDERATE_REMEMBER_TIMEOUT', 604800) # one week
|
setting_default('CAS_FEDERATE_REMEMBER_TIMEOUT', 604800) # one week
|
||||||
|
|
||||||
if settings.CAS_FEDERATE:
|
if settings.CAS_FEDERATE:
|
||||||
settings.CAS_AUTH_CLASS = "cas_server.auth.CASFederateAuth"
|
settings.CAS_AUTH_CLASS = "cas_server.auth.CASFederateAuth"
|
||||||
|
|
||||||
# create CAS_FEDERATE_PROVIDERS_LIST default value if not set: list of
|
|
||||||
# the keys of CAS_FEDERATE_PROVIDERS in natural order: 2 < 10 < 20 < a = A < … < z = Z
|
|
||||||
try:
|
|
||||||
getattr(settings, 'CAS_FEDERATE_PROVIDERS_LIST')
|
|
||||||
except AttributeError:
|
|
||||||
__CAS_FEDERATE_PROVIDERS_LIST = list(settings.CAS_FEDERATE_PROVIDERS.keys())
|
|
||||||
|
|
||||||
def __cas_federate_providers_list_sort(key):
|
|
||||||
if len(settings.CAS_FEDERATE_PROVIDERS[key]) > 2:
|
|
||||||
key = settings.CAS_FEDERATE_PROVIDERS[key][2].lower()
|
|
||||||
else:
|
|
||||||
key = key.lower()
|
|
||||||
return tuple(
|
|
||||||
int(num) if num else alpha
|
|
||||||
for num, alpha in __cas_federate_providers_list_sort.tokenize(key)
|
|
||||||
)
|
|
||||||
__cas_federate_providers_list_sort.tokenize = re.compile(r'(\d+)|(\D+)').findall
|
|
||||||
__CAS_FEDERATE_PROVIDERS_LIST.sort(key=__cas_federate_providers_list_sort)
|
|
||||||
|
|
||||||
setting_default('CAS_FEDERATE_PROVIDERS_LIST', __CAS_FEDERATE_PROVIDERS_LIST)
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
# (c) 2016 Valentin Samir
|
# (c) 2016 Valentin Samir
|
||||||
"""federated mode helper classes"""
|
"""federated mode helper classes"""
|
||||||
from .default_settings import settings
|
from .default_settings import settings
|
||||||
|
from django.db import IntegrityError
|
||||||
|
|
||||||
from .cas import CASClient
|
from .cas import CASClient
|
||||||
from .models import FederatedUser, FederateSLO, User
|
from .models import FederatedUser, FederateSLO, User
|
||||||
|
@ -29,28 +30,23 @@ class CASFederateValidateUser(object):
|
||||||
|
|
||||||
def __init__(self, provider, service_url):
|
def __init__(self, provider, service_url):
|
||||||
self.provider = provider
|
self.provider = provider
|
||||||
|
self.client = CASClient(
|
||||||
if provider in settings.CAS_FEDERATE_PROVIDERS: # pragma: no branch (should always be True)
|
service_url=service_url,
|
||||||
(server_url, version) = settings.CAS_FEDERATE_PROVIDERS[provider][:2]
|
version=provider.cas_protocol_version,
|
||||||
self.client = CASClient(
|
server_url=provider.server_url,
|
||||||
service_url=service_url,
|
renew=False,
|
||||||
version=version,
|
)
|
||||||
server_url=server_url,
|
|
||||||
renew=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_login_url(self):
|
def get_login_url(self):
|
||||||
"""return the CAS provider login url"""
|
"""return the CAS provider login url"""
|
||||||
return self.client.get_login_url() if self.client is not None else False
|
return self.client.get_login_url()
|
||||||
|
|
||||||
def get_logout_url(self, redirect_url=None):
|
def get_logout_url(self, redirect_url=None):
|
||||||
"""return the CAS provider logout url"""
|
"""return the CAS provider logout url"""
|
||||||
return self.client.get_logout_url(redirect_url) if self.client is not None else False
|
return self.client.get_logout_url(redirect_url)
|
||||||
|
|
||||||
def verify_ticket(self, ticket):
|
def verify_ticket(self, ticket):
|
||||||
"""test `ticket` agains the CAS provider, if valid, create the local federated user"""
|
"""test `ticket` agains the CAS provider, if valid, create the local federated user"""
|
||||||
if self.client is None: # pragma: no cover (should not happen)
|
|
||||||
return False
|
|
||||||
try:
|
try:
|
||||||
username, attributs = self.client.verify_ticket(ticket)[:2]
|
username, attributs = self.client.verify_ticket(ticket)[:2]
|
||||||
except urllib.error.URLError:
|
except urllib.error.URLError:
|
||||||
|
@ -61,22 +57,13 @@ class CASFederateValidateUser(object):
|
||||||
attributs["provider"] = self.provider
|
attributs["provider"] = self.provider
|
||||||
self.username = username
|
self.username = username
|
||||||
self.attributs = attributs
|
self.attributs = attributs
|
||||||
try:
|
user = FederatedUser.objects.update_or_create(
|
||||||
user = FederatedUser.objects.get(
|
username=username,
|
||||||
username=username,
|
provider=self.provider,
|
||||||
provider=self.provider
|
defaults=dict(attributs=attributs, ticket=ticket)
|
||||||
)
|
)[0]
|
||||||
user.attributs = attributs
|
user.save()
|
||||||
user.ticket = ticket
|
self.federated_username = user.federated_username
|
||||||
user.save()
|
|
||||||
except FederatedUser.DoesNotExist:
|
|
||||||
user = FederatedUser.objects.create(
|
|
||||||
username=username,
|
|
||||||
provider=self.provider,
|
|
||||||
attributs=attributs,
|
|
||||||
ticket=ticket
|
|
||||||
)
|
|
||||||
user.save()
|
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
@ -84,11 +71,14 @@ class CASFederateValidateUser(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def register_slo(username, session_key, ticket):
|
def register_slo(username, session_key, ticket):
|
||||||
"""association a ticket with a (username, session) for processing later SLO request"""
|
"""association a ticket with a (username, session) for processing later SLO request"""
|
||||||
FederateSLO.objects.create(
|
try:
|
||||||
username=username,
|
FederateSLO.objects.create(
|
||||||
session_key=session_key,
|
username=username,
|
||||||
ticket=ticket
|
session_key=session_key,
|
||||||
)
|
ticket=ticket
|
||||||
|
)
|
||||||
|
except IntegrityError: # pragma: no cover (ignore if the FederateSLO already exists)
|
||||||
|
pass
|
||||||
|
|
||||||
def clean_sessions(self, logout_request):
|
def clean_sessions(self, logout_request):
|
||||||
"""process a SLO request"""
|
"""process a SLO request"""
|
||||||
|
|
|
@ -33,16 +33,14 @@ class FederateSelect(forms.Form):
|
||||||
Form used on the login page when CAS_FEDERATE is True
|
Form used on the login page when CAS_FEDERATE is True
|
||||||
allowing the user to choose a identity provider.
|
allowing the user to choose a identity provider.
|
||||||
"""
|
"""
|
||||||
provider = forms.ChoiceField(
|
provider = forms.ModelChoiceField(
|
||||||
|
queryset=models.FederatedIendityProvider.objects.all().order_by(
|
||||||
|
"pos",
|
||||||
|
"verbose_name",
|
||||||
|
"suffix"
|
||||||
|
),
|
||||||
|
to_field_name="suffix",
|
||||||
label=_('Identity provider'),
|
label=_('Identity provider'),
|
||||||
# with use a lambda abstraction to delay the access to settings.CAS_FEDERATE_PROVIDERS
|
|
||||||
# this is usefull to use the override_settings decorator in tests
|
|
||||||
choices=[
|
|
||||||
(
|
|
||||||
p,
|
|
||||||
utils.get_tuple(settings.CAS_FEDERATE_PROVIDERS[p], 2, p)
|
|
||||||
) for p in settings.CAS_FEDERATE_PROVIDERS_LIST
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
service = forms.CharField(label=_('service'), widget=forms.HiddenInput(), required=False)
|
service = forms.CharField(label=_('service'), widget=forms.HiddenInput(), required=False)
|
||||||
method = forms.CharField(widget=forms.HiddenInput(), required=False)
|
method = forms.CharField(widget=forms.HiddenInput(), required=False)
|
||||||
|
@ -88,13 +86,10 @@ class FederateUserCredential(UserCredential):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
cleaned_data = super(FederateUserCredential, self).clean()
|
cleaned_data = super(FederateUserCredential, self).clean()
|
||||||
try:
|
try:
|
||||||
component = cleaned_data["username"].split('@')
|
user = models.FederatedUser.get_from_federated_username(cleaned_data["username"])
|
||||||
username = '@'.join(component[:-1])
|
|
||||||
provider = component[-1]
|
|
||||||
user = models.FederatedUser.objects.get(username=username, provider=provider)
|
|
||||||
user.ticket = ""
|
user.ticket = ""
|
||||||
user.save()
|
user.save()
|
||||||
# should not happed as is the FederatedUser do not exists, super should
|
# should not happed as if the FederatedUser do not exists, super should
|
||||||
# raise before a ValidationError("bad user")
|
# raise before a ValidationError("bad user")
|
||||||
except models.FederatedUser.DoesNotExist: # pragma: no cover (should not happend)
|
except models.FederatedUser.DoesNotExist: # pragma: no cover (should not happend)
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
|
|
Binary file not shown.
|
@ -7,8 +7,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: cas_server\n"
|
"Project-Id-Version: cas_server\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2016-06-21 00:14+0200\n"
|
"POT-Creation-Date: 2016-07-04 17:15+0200\n"
|
||||||
"PO-Revision-Date: 2016-06-21 00:16+0200\n"
|
"PO-Revision-Date: 2016-07-04 17:15+0200\n"
|
||||||
"Last-Translator: Valentin Samir <valentin.samir@crans.org>\n"
|
"Last-Translator: Valentin Samir <valentin.samir@crans.org>\n"
|
||||||
"Language-Team: django <LL@li.org>\n"
|
"Language-Team: django <LL@li.org>\n"
|
||||||
"Language: en\n"
|
"Language: en\n"
|
||||||
|
@ -17,88 +17,135 @@ msgstr ""
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Generator: Poedit 1.8.8\n"
|
"X-Generator: Poedit 1.8.8\n"
|
||||||
|
|
||||||
#: apps.py:7 templates/cas_server/base.html:3 templates/cas_server/base.html:21
|
#: apps.py:19 templates/cas_server/base.html:3
|
||||||
|
#: templates/cas_server/base.html:20
|
||||||
msgid "Central Authentication Service"
|
msgid "Central Authentication Service"
|
||||||
msgstr "Central Authentication Service"
|
msgstr "Central Authentication Service"
|
||||||
|
|
||||||
#: forms.py:32
|
#: forms.py:43
|
||||||
msgid "Identity provider"
|
msgid "Identity provider"
|
||||||
msgstr "Identity provider"
|
msgstr "Identity provider"
|
||||||
|
|
||||||
#: forms.py:35 forms.py:44 forms.py:92
|
#: forms.py:45 forms.py:55 forms.py:106
|
||||||
msgid "service"
|
msgid "service"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:37
|
#: forms.py:47
|
||||||
msgid "Remember the identity provider"
|
msgid "Remember the identity provider"
|
||||||
msgstr "Remember the identity provider"
|
msgstr "Remember the identity provider"
|
||||||
|
|
||||||
#: forms.py:38 forms.py:48
|
#: forms.py:48 forms.py:59
|
||||||
msgid "warn"
|
msgid "warn"
|
||||||
msgstr " Warn me before logging me into other sites."
|
msgstr " Warn me before logging me into other sites."
|
||||||
|
|
||||||
#: forms.py:43
|
#: forms.py:54
|
||||||
msgid "login"
|
msgid "login"
|
||||||
msgstr "username"
|
msgstr "username"
|
||||||
|
|
||||||
#: forms.py:45
|
#: forms.py:56
|
||||||
msgid "password"
|
msgid "password"
|
||||||
msgstr "password"
|
msgstr "password"
|
||||||
|
|
||||||
#: forms.py:59
|
#: forms.py:71
|
||||||
msgid "Bad user"
|
msgid "Bad user"
|
||||||
msgstr "The credentials you provided cannot be determined to be authentic."
|
msgstr "The credentials you provided cannot be determined to be authentic."
|
||||||
|
|
||||||
#: management/commands/cas_clean_federate.py:13
|
#: forms.py:96
|
||||||
|
msgid "User not found in the temporary database, please try to reconnect"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: management/commands/cas_clean_federate.py:20
|
||||||
msgid "Clean old federated users"
|
msgid "Clean old federated users"
|
||||||
msgstr "Clean old federated users"
|
msgstr "Clean old federated users"
|
||||||
|
|
||||||
#: management/commands/cas_clean_sessions.py:9
|
#: management/commands/cas_clean_sessions.py:22
|
||||||
msgid "Clean deleted sessions"
|
msgid "Clean deleted sessions"
|
||||||
msgstr "Clean deleted sessions"
|
msgstr "Clean deleted sessions"
|
||||||
|
|
||||||
#: management/commands/cas_clean_tickets.py:9
|
#: management/commands/cas_clean_tickets.py:22
|
||||||
msgid "Clean old trickets"
|
msgid "Clean old trickets"
|
||||||
msgstr "Clean old trickets"
|
msgstr "Clean old trickets"
|
||||||
|
|
||||||
#: models.py:55
|
#: models.py:42
|
||||||
|
msgid "identity provider"
|
||||||
|
msgstr "identity provider"
|
||||||
|
|
||||||
|
#: models.py:43
|
||||||
|
msgid "identity providers"
|
||||||
|
msgstr "identity providers"
|
||||||
|
|
||||||
|
#: models.py:47
|
||||||
|
msgid "suffix"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:48
|
||||||
|
msgid ""
|
||||||
|
"Suffix append to backend CAS returner username: `returned_username`@`suffix`"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:50
|
||||||
|
msgid "server url"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:59
|
||||||
|
msgid "CAS protocol version"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:60
|
||||||
|
msgid ""
|
||||||
|
"Version of the CAS protocol to use when sending requests the the backend CAS"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:65
|
||||||
|
msgid "verbose name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:66
|
||||||
|
msgid "Name for this identity provider displayed on the login page"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models.py:70 models.py:312
|
||||||
|
msgid "position"
|
||||||
|
msgstr "position"
|
||||||
|
|
||||||
|
#: models.py:159
|
||||||
msgid "User"
|
msgid "User"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: models.py:56
|
#: models.py:160
|
||||||
msgid "Users"
|
msgid "Users"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: models.py:114
|
#: models.py:229
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Error during service logout %s"
|
msgid "Error during service logout %s"
|
||||||
msgstr "Error during service logout %s"
|
msgstr "Error during service logout %s"
|
||||||
|
|
||||||
#: models.py:182
|
#: models.py:307
|
||||||
msgid "Service pattern"
|
msgid "Service pattern"
|
||||||
msgstr "Service pattern"
|
msgstr "Service pattern"
|
||||||
|
|
||||||
#: models.py:183
|
#: models.py:308
|
||||||
msgid "Services patterns"
|
msgid "Services patterns"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: models.py:187
|
#: models.py:313
|
||||||
msgid "position"
|
msgid "service patterns are sorted using the position attribute"
|
||||||
msgstr "position"
|
msgstr ""
|
||||||
|
|
||||||
#: models.py:194 models.py:316
|
#: models.py:320 models.py:444
|
||||||
msgid "name"
|
msgid "name"
|
||||||
msgstr "name"
|
msgstr "name"
|
||||||
|
|
||||||
#: models.py:195
|
#: models.py:321
|
||||||
msgid "A name for the service"
|
msgid "A name for the service"
|
||||||
msgstr "A name for the service"
|
msgstr "A name for the service"
|
||||||
|
|
||||||
#: models.py:200 models.py:344 models.py:362
|
#: models.py:326 models.py:473 models.py:492
|
||||||
msgid "pattern"
|
msgid "pattern"
|
||||||
msgstr "pattern"
|
msgstr "pattern"
|
||||||
|
|
||||||
#: models.py:202
|
#: models.py:328
|
||||||
msgid ""
|
msgid ""
|
||||||
"A regular expression matching services. Will usually looks like '^https://"
|
"A regular expression matching services. Will usually looks like '^https://"
|
||||||
"some\\.server\\.com/path/.*$'.As it is a regular expression, special "
|
"some\\.server\\.com/path/.*$'.As it is a regular expression, special "
|
||||||
|
@ -108,73 +155,73 @@ msgstr ""
|
||||||
"some\\.server\\.com/path/.*$'.As it is a regular expression, special "
|
"some\\.server\\.com/path/.*$'.As it is a regular expression, special "
|
||||||
"character must be escaped with a '\\'."
|
"character must be escaped with a '\\'."
|
||||||
|
|
||||||
#: models.py:211
|
#: models.py:337
|
||||||
msgid "user field"
|
msgid "user field"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: models.py:212
|
#: models.py:338
|
||||||
msgid "Name of the attribut to transmit as username, empty = login"
|
msgid "Name of the attribut to transmit as username, empty = login"
|
||||||
msgstr "Name of the attribut to transmit as username, empty = login"
|
msgstr "Name of the attribut to transmit as username, empty = login"
|
||||||
|
|
||||||
#: models.py:216
|
#: models.py:342
|
||||||
msgid "restrict username"
|
msgid "restrict username"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: models.py:217
|
#: models.py:343
|
||||||
msgid "Limit username allowed to connect to the list provided bellow"
|
msgid "Limit username allowed to connect to the list provided bellow"
|
||||||
msgstr "Limit username allowed to connect to the list provided bellow"
|
msgstr "Limit username allowed to connect to the list provided bellow"
|
||||||
|
|
||||||
#: models.py:221
|
#: models.py:347
|
||||||
msgid "proxy"
|
msgid "proxy"
|
||||||
msgstr "proxy"
|
msgstr "proxy"
|
||||||
|
|
||||||
#: models.py:222
|
#: models.py:348
|
||||||
msgid "Proxy tickets can be delivered to the service"
|
msgid "Proxy tickets can be delivered to the service"
|
||||||
msgstr "Proxy tickets can be delivered to the service"
|
msgstr "Proxy tickets can be delivered to the service"
|
||||||
|
|
||||||
#: models.py:226
|
#: models.py:352
|
||||||
msgid "proxy callback"
|
msgid "proxy callback"
|
||||||
msgstr "proxy callback"
|
msgstr "proxy callback"
|
||||||
|
|
||||||
#: models.py:227
|
#: models.py:353
|
||||||
msgid "can be used as a proxy callback to deliver PGT"
|
msgid "can be used as a proxy callback to deliver PGT"
|
||||||
msgstr "can be used as a proxy callback to deliver PGT"
|
msgstr "can be used as a proxy callback to deliver PGT"
|
||||||
|
|
||||||
#: models.py:231
|
#: models.py:357
|
||||||
msgid "single log out"
|
msgid "single log out"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: models.py:232
|
#: models.py:358
|
||||||
msgid "Enable SLO for the service"
|
msgid "Enable SLO for the service"
|
||||||
msgstr "Enable SLO for the service"
|
msgstr "Enable SLO for the service"
|
||||||
|
|
||||||
#: models.py:239
|
#: models.py:365
|
||||||
msgid "single log out callback"
|
msgid "single log out callback"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: models.py:240
|
#: models.py:366
|
||||||
msgid ""
|
msgid ""
|
||||||
"URL where the SLO request will be POST. empty = service url\n"
|
"URL where the SLO request will be POST. empty = service url\n"
|
||||||
"This is usefull for non HTTP proxied services."
|
"This is usefull for non HTTP proxied services."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: models.py:301
|
#: models.py:428
|
||||||
msgid "username"
|
msgid "username"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: models.py:302
|
#: models.py:429
|
||||||
msgid "username allowed to connect to the service"
|
msgid "username allowed to connect to the service"
|
||||||
msgstr "username allowed to connect to the service"
|
msgstr "username allowed to connect to the service"
|
||||||
|
|
||||||
#: models.py:317
|
#: models.py:445
|
||||||
msgid "name of an attribut to send to the service, use * for all attributes"
|
msgid "name of an attribut to send to the service, use * for all attributes"
|
||||||
msgstr "name of an attribut to send to the service, use * for all attributes"
|
msgstr "name of an attribut to send to the service, use * for all attributes"
|
||||||
|
|
||||||
#: models.py:322 models.py:368
|
#: models.py:450 models.py:498
|
||||||
msgid "replace"
|
msgid "replace"
|
||||||
msgstr "replace"
|
msgstr "replace"
|
||||||
|
|
||||||
#: models.py:323
|
#: models.py:451
|
||||||
msgid ""
|
msgid ""
|
||||||
"name under which the attribut will be showto the service. empty = default "
|
"name under which the attribut will be showto the service. empty = default "
|
||||||
"name of the attribut"
|
"name of the attribut"
|
||||||
|
@ -182,39 +229,30 @@ msgstr ""
|
||||||
"name under which the attribut will be showto the service. empty = default "
|
"name under which the attribut will be showto the service. empty = default "
|
||||||
"name of the attribut"
|
"name of the attribut"
|
||||||
|
|
||||||
#: models.py:339 models.py:357
|
#: models.py:468 models.py:487
|
||||||
msgid "attribut"
|
msgid "attribut"
|
||||||
msgstr "attribut"
|
msgstr "attribut"
|
||||||
|
|
||||||
#: models.py:340
|
#: models.py:469
|
||||||
msgid "Name of the attribut which must verify pattern"
|
msgid "Name of the attribut which must verify pattern"
|
||||||
msgstr "Name of the attribut which must verify pattern"
|
msgstr "Name of the attribut which must verify pattern"
|
||||||
|
|
||||||
#: models.py:345
|
#: models.py:474
|
||||||
msgid "a regular expression"
|
msgid "a regular expression"
|
||||||
msgstr "a regular expression"
|
msgstr "a regular expression"
|
||||||
|
|
||||||
#: models.py:358
|
#: models.py:488
|
||||||
msgid "Name of the attribut for which the value must be replace"
|
msgid "Name of the attribut for which the value must be replace"
|
||||||
msgstr "Name of the attribut for which the value must be replace"
|
msgstr "Name of the attribut for which the value must be replace"
|
||||||
|
|
||||||
#: models.py:363
|
#: models.py:493
|
||||||
msgid "An regular expression maching whats need to be replaced"
|
msgid "An regular expression maching whats need to be replaced"
|
||||||
msgstr "An regular expression maching whats need to be replaced"
|
msgstr "An regular expression maching whats need to be replaced"
|
||||||
|
|
||||||
#: models.py:369
|
#: models.py:499
|
||||||
msgid "replace expression, groups are capture by \\1, \\2 …"
|
msgid "replace expression, groups are capture by \\1, \\2 …"
|
||||||
msgstr "replace expression, groups are capture by \\1, \\2 …"
|
msgstr "replace expression, groups are capture by \\1, \\2 …"
|
||||||
|
|
||||||
#: models.py:476
|
|
||||||
#, python-format
|
|
||||||
msgid ""
|
|
||||||
"Error during service logout %(service)s:\n"
|
|
||||||
"%(error)s"
|
|
||||||
msgstr ""
|
|
||||||
"Error during service logout %(service)s:\n"
|
|
||||||
"%(error)s"
|
|
||||||
|
|
||||||
#: templates/cas_server/logged.html:6
|
#: templates/cas_server/logged.html:6
|
||||||
msgid "Logged"
|
msgid "Logged"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -243,7 +281,7 @@ msgstr "Login"
|
||||||
msgid "Connect to the service"
|
msgid "Connect to the service"
|
||||||
msgstr "Connect to the service"
|
msgstr "Connect to the service"
|
||||||
|
|
||||||
#: views.py:140
|
#: views.py:152
|
||||||
msgid ""
|
msgid ""
|
||||||
"<h3>Logout successful</h3>You have successfully logged out from the Central "
|
"<h3>Logout successful</h3>You have successfully logged out from the Central "
|
||||||
"Authentication Service. For security reasons, exit your web browser."
|
"Authentication Service. For security reasons, exit your web browser."
|
||||||
|
@ -251,7 +289,7 @@ msgstr ""
|
||||||
"<h3>Logout successful</h3>You have successfully logged out from the Central "
|
"<h3>Logout successful</h3>You have successfully logged out from the Central "
|
||||||
"Authentication Service. For security reasons, exit your web browser."
|
"Authentication Service. For security reasons, exit your web browser."
|
||||||
|
|
||||||
#: views.py:146
|
#: views.py:158
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"<h3>Logout successful</h3>You have successfully logged out from %s sessions "
|
"<h3>Logout successful</h3>You have successfully logged out from %s sessions "
|
||||||
|
@ -262,7 +300,7 @@ msgstr ""
|
||||||
"of the Central Authentication Service. For security reasons, exit your web "
|
"of the Central Authentication Service. For security reasons, exit your web "
|
||||||
"browser."
|
"browser."
|
||||||
|
|
||||||
#: views.py:153
|
#: views.py:165
|
||||||
msgid ""
|
msgid ""
|
||||||
"<h3>Logout successful</h3>You were already logged out from the Central "
|
"<h3>Logout successful</h3>You were already logged out from the Central "
|
||||||
"Authentication Service. For security reasons, exit your web browser."
|
"Authentication Service. For security reasons, exit your web browser."
|
||||||
|
@ -270,48 +308,55 @@ msgstr ""
|
||||||
"<h3>Logout successful</h3>You were already logged out from the Central "
|
"<h3>Logout successful</h3>You were already logged out from the Central "
|
||||||
"Authentication Service. For security reasons, exit your web browser."
|
"Authentication Service. For security reasons, exit your web browser."
|
||||||
|
|
||||||
#: views.py:294
|
#: views.py:349
|
||||||
msgid "Invalid login ticket"
|
msgid "Invalid login ticket"
|
||||||
msgstr "Invalid login ticket, please retry to login"
|
msgstr "Invalid login ticket, please retry to login"
|
||||||
|
|
||||||
#: views.py:410
|
#: views.py:470
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Authentication has been required by service %(name)s (%(url)s)"
|
msgid "Authentication has been required by service %(name)s (%(url)s)"
|
||||||
msgstr "Authentication has been required by service %(name)s (%(url)s)"
|
msgstr "Authentication has been required by service %(name)s (%(url)s)"
|
||||||
|
|
||||||
#: views.py:448
|
#: views.py:508
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Service %(url)s non allowed."
|
msgid "Service %(url)s non allowed."
|
||||||
msgstr "Service %(url)s non allowed."
|
msgstr "Service %(url)s non allowed."
|
||||||
|
|
||||||
#: views.py:455
|
#: views.py:515
|
||||||
msgid "Username non allowed"
|
msgid "Username non allowed"
|
||||||
msgstr "Username non allowed"
|
msgstr "Username non allowed"
|
||||||
|
|
||||||
#: views.py:462
|
#: views.py:522
|
||||||
msgid "User charateristics non allowed"
|
msgid "User charateristics non allowed"
|
||||||
msgstr "User charateristics non allowed"
|
msgstr "User charateristics non allowed"
|
||||||
|
|
||||||
#: views.py:469
|
#: views.py:529
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "The attribut %(field)s is needed to use that service"
|
msgid "The attribut %(field)s is needed to use that service"
|
||||||
msgstr "The attribut %(field)s is needed to use that service"
|
msgstr "The attribut %(field)s is needed to use that service"
|
||||||
|
|
||||||
#: views.py:539
|
#: views.py:599
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Authentication renewal required by service %(name)s (%(url)s)."
|
msgid "Authentication renewal required by service %(name)s (%(url)s)."
|
||||||
msgstr "Authentication renewal required by service %(name)s (%(url)s)."
|
msgstr "Authentication renewal required by service %(name)s (%(url)s)."
|
||||||
|
|
||||||
#: views.py:546
|
#: views.py:606
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Authentication required by service %(name)s (%(url)s)."
|
msgid "Authentication required by service %(name)s (%(url)s)."
|
||||||
msgstr "Authentication required by service %(name)s (%(url)s)."
|
msgstr "Authentication required by service %(name)s (%(url)s)."
|
||||||
|
|
||||||
#: views.py:553
|
#: views.py:613
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Service %s non allowed"
|
msgid "Service %s non allowed"
|
||||||
msgstr "Service %s non allowed"
|
msgstr "Service %s non allowed"
|
||||||
|
|
||||||
|
#~ msgid ""
|
||||||
|
#~ "Error during service logout %(service)s:\n"
|
||||||
|
#~ "%(error)s"
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "Error during service logout %(service)s:\n"
|
||||||
|
#~ "%(error)s"
|
||||||
|
|
||||||
#~ msgid "Successfully logout"
|
#~ msgid "Successfully logout"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "<h3>Logout successful</h3>You have successfully logged out of the Central "
|
#~ "<h3>Logout successful</h3>You have successfully logged out of the Central "
|
||||||
|
|
Binary file not shown.
|
@ -7,8 +7,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: cas_server\n"
|
"Project-Id-Version: cas_server\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2016-06-21 00:14+0200\n"
|
"POT-Creation-Date: 2016-07-04 17:15+0200\n"
|
||||||
"PO-Revision-Date: 2016-06-21 00:15+0200\n"
|
"PO-Revision-Date: 2016-07-04 17:21+0200\n"
|
||||||
"Last-Translator: Valentin Samir <valentin.samir@crans.org>\n"
|
"Last-Translator: Valentin Samir <valentin.samir@crans.org>\n"
|
||||||
"Language-Team: django <LL@li.org>\n"
|
"Language-Team: django <LL@li.org>\n"
|
||||||
"Language: fr\n"
|
"Language: fr\n"
|
||||||
|
@ -18,88 +18,141 @@ msgstr ""
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
"X-Generator: Poedit 1.8.8\n"
|
"X-Generator: Poedit 1.8.8\n"
|
||||||
|
|
||||||
#: apps.py:7 templates/cas_server/base.html:3 templates/cas_server/base.html:21
|
#: apps.py:19 templates/cas_server/base.html:3
|
||||||
|
#: templates/cas_server/base.html:20
|
||||||
msgid "Central Authentication Service"
|
msgid "Central Authentication Service"
|
||||||
msgstr "Service Central d'Authentification"
|
msgstr "Service Central d'Authentification"
|
||||||
|
|
||||||
#: forms.py:32
|
#: forms.py:43
|
||||||
msgid "Identity provider"
|
msgid "Identity provider"
|
||||||
msgstr "fournisseur d'identité"
|
msgstr "fournisseur d'identité"
|
||||||
|
|
||||||
#: forms.py:35 forms.py:44 forms.py:92
|
#: forms.py:45 forms.py:55 forms.py:106
|
||||||
msgid "service"
|
msgid "service"
|
||||||
msgstr "service"
|
msgstr "service"
|
||||||
|
|
||||||
#: forms.py:37
|
#: forms.py:47
|
||||||
msgid "Remember the identity provider"
|
msgid "Remember the identity provider"
|
||||||
msgstr "Se souvenir du fournisseur d'identité"
|
msgstr "Se souvenir du fournisseur d'identité"
|
||||||
|
|
||||||
#: forms.py:38 forms.py:48
|
#: forms.py:48 forms.py:59
|
||||||
msgid "warn"
|
msgid "warn"
|
||||||
msgstr "Prévenez-moi avant d'accéder à d'autres services."
|
msgstr "Prévenez-moi avant d'accéder à d'autres services."
|
||||||
|
|
||||||
#: forms.py:43
|
#: forms.py:54
|
||||||
msgid "login"
|
msgid "login"
|
||||||
msgstr "Identifiant"
|
msgstr "Identifiant"
|
||||||
|
|
||||||
#: forms.py:45
|
#: forms.py:56
|
||||||
msgid "password"
|
msgid "password"
|
||||||
msgstr "mot de passe"
|
msgstr "mot de passe"
|
||||||
|
|
||||||
#: forms.py:59
|
#: forms.py:71
|
||||||
msgid "Bad user"
|
msgid "Bad user"
|
||||||
msgstr "Les informations transmises n'ont pas permis de vous authentifier."
|
msgstr "Les informations transmises n'ont pas permis de vous authentifier."
|
||||||
|
|
||||||
#: management/commands/cas_clean_federate.py:13
|
#: forms.py:96
|
||||||
|
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"
|
||||||
|
|
||||||
|
#: management/commands/cas_clean_federate.py:20
|
||||||
msgid "Clean old federated users"
|
msgid "Clean old federated users"
|
||||||
msgstr "Nettoyer les anciens utilisateurs fédéré"
|
msgstr "Nettoyer les anciens utilisateurs fédéré"
|
||||||
|
|
||||||
#: management/commands/cas_clean_sessions.py:9
|
#: management/commands/cas_clean_sessions.py:22
|
||||||
msgid "Clean deleted sessions"
|
msgid "Clean deleted sessions"
|
||||||
msgstr "Nettoyer les sessions supprimées"
|
msgstr "Nettoyer les sessions supprimées"
|
||||||
|
|
||||||
#: management/commands/cas_clean_tickets.py:9
|
#: management/commands/cas_clean_tickets.py:22
|
||||||
msgid "Clean old trickets"
|
msgid "Clean old trickets"
|
||||||
msgstr "Nettoyer les vieux tickets"
|
msgstr "Nettoyer les vieux tickets"
|
||||||
|
|
||||||
#: models.py:55
|
#: models.py:42
|
||||||
|
msgid "identity provider"
|
||||||
|
msgstr "fournisseur d'identité"
|
||||||
|
|
||||||
|
#: models.py:43
|
||||||
|
msgid "identity providers"
|
||||||
|
msgstr "fournisseurs d'identités"
|
||||||
|
|
||||||
|
#: models.py:47
|
||||||
|
msgid "suffix"
|
||||||
|
msgstr "suffixe"
|
||||||
|
|
||||||
|
#: models.py:48
|
||||||
|
msgid ""
|
||||||
|
"Suffix append to backend CAS returner username: `returned_username`@`suffix`"
|
||||||
|
msgstr ""
|
||||||
|
"Suffixe ajouté au nom d'utilisateur retourné par le CAS du fournisseur "
|
||||||
|
"d'identité : `nom retourné`@`suffixe`"
|
||||||
|
|
||||||
|
#: models.py:50
|
||||||
|
msgid "server url"
|
||||||
|
msgstr "url du serveur"
|
||||||
|
|
||||||
|
#: models.py:59
|
||||||
|
msgid "CAS protocol version"
|
||||||
|
msgstr "Version du protocole CAS"
|
||||||
|
|
||||||
|
#: models.py:60
|
||||||
|
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:65
|
||||||
|
msgid "verbose name"
|
||||||
|
msgstr "Nom du fournisseur"
|
||||||
|
|
||||||
|
#: models.py:66
|
||||||
|
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:70 models.py:312
|
||||||
|
msgid "position"
|
||||||
|
msgstr "position"
|
||||||
|
|
||||||
|
#: models.py:159
|
||||||
msgid "User"
|
msgid "User"
|
||||||
msgstr "Utilisateur"
|
msgstr "Utilisateur"
|
||||||
|
|
||||||
#: models.py:56
|
#: models.py:160
|
||||||
msgid "Users"
|
msgid "Users"
|
||||||
msgstr "Utilisateurs"
|
msgstr "Utilisateurs"
|
||||||
|
|
||||||
#: models.py:114
|
#: models.py:229
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Error during service logout %s"
|
msgid "Error during service logout %s"
|
||||||
msgstr "Une erreur est survenue durant la déconnexion du service %s"
|
msgstr "Une erreur est survenue durant la déconnexion du service %s"
|
||||||
|
|
||||||
#: models.py:182
|
#: models.py:307
|
||||||
msgid "Service pattern"
|
msgid "Service pattern"
|
||||||
msgstr "Motif de service"
|
msgstr "Motif de service"
|
||||||
|
|
||||||
#: models.py:183
|
#: models.py:308
|
||||||
msgid "Services patterns"
|
msgid "Services patterns"
|
||||||
msgstr "Motifs de services"
|
msgstr "Motifs de services"
|
||||||
|
|
||||||
#: models.py:187
|
#: models.py:313
|
||||||
msgid "position"
|
msgid "service patterns are sorted using the position attribute"
|
||||||
msgstr "position"
|
msgstr "Les motifs de service sont trié selon l'attribut position"
|
||||||
|
|
||||||
#: models.py:194 models.py:316
|
#: models.py:320 models.py:444
|
||||||
msgid "name"
|
msgid "name"
|
||||||
msgstr "nom"
|
msgstr "nom"
|
||||||
|
|
||||||
#: models.py:195
|
#: models.py:321
|
||||||
msgid "A name for the service"
|
msgid "A name for the service"
|
||||||
msgstr "Un nom pour le service"
|
msgstr "Un nom pour le service"
|
||||||
|
|
||||||
#: models.py:200 models.py:344 models.py:362
|
#: models.py:326 models.py:473 models.py:492
|
||||||
msgid "pattern"
|
msgid "pattern"
|
||||||
msgstr "motif"
|
msgstr "motif"
|
||||||
|
|
||||||
#: models.py:202
|
#: models.py:328
|
||||||
msgid ""
|
msgid ""
|
||||||
"A regular expression matching services. Will usually looks like '^https://"
|
"A regular expression matching services. Will usually looks like '^https://"
|
||||||
"some\\.server\\.com/path/.*$'.As it is a regular expression, special "
|
"some\\.server\\.com/path/.*$'.As it is a regular expression, special "
|
||||||
|
@ -110,55 +163,55 @@ msgstr ""
|
||||||
"expression rationnelle, les caractères spéciaux doivent être échappés avec "
|
"expression rationnelle, les caractères spéciaux doivent être échappés avec "
|
||||||
"un '\\'."
|
"un '\\'."
|
||||||
|
|
||||||
#: models.py:211
|
#: models.py:337
|
||||||
msgid "user field"
|
msgid "user field"
|
||||||
msgstr "champ utilisateur"
|
msgstr "champ utilisateur"
|
||||||
|
|
||||||
#: models.py:212
|
#: models.py:338
|
||||||
msgid "Name of the attribut to transmit as username, empty = login"
|
msgid "Name of the attribut to transmit as username, empty = login"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Nom de l'attribut devant être transmis comme nom d'utilisateur au service. "
|
"Nom de l'attribut devant être transmis comme nom d'utilisateur au service. "
|
||||||
"vide = nom de connection"
|
"vide = nom de connection"
|
||||||
|
|
||||||
#: models.py:216
|
#: models.py:342
|
||||||
msgid "restrict username"
|
msgid "restrict username"
|
||||||
msgstr "limiter les noms d'utilisateurs"
|
msgstr "limiter les noms d'utilisateurs"
|
||||||
|
|
||||||
#: models.py:217
|
#: models.py:343
|
||||||
msgid "Limit username allowed to connect to the list provided bellow"
|
msgid "Limit username allowed to connect to the list provided bellow"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Limiter les noms d'utilisateurs autorisé à se connecter à la liste fournie "
|
"Limiter les noms d'utilisateurs autorisé à se connecter à la liste fournie "
|
||||||
"ci-dessous"
|
"ci-dessous"
|
||||||
|
|
||||||
#: models.py:221
|
#: models.py:347
|
||||||
msgid "proxy"
|
msgid "proxy"
|
||||||
msgstr "proxy"
|
msgstr "proxy"
|
||||||
|
|
||||||
#: models.py:222
|
#: models.py:348
|
||||||
msgid "Proxy tickets can be delivered to the service"
|
msgid "Proxy tickets can be delivered to the service"
|
||||||
msgstr "des proxy tickets peuvent être délivrés au service"
|
msgstr "des proxy tickets peuvent être délivrés au service"
|
||||||
|
|
||||||
#: models.py:226
|
#: models.py:352
|
||||||
msgid "proxy callback"
|
msgid "proxy callback"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: models.py:227
|
#: models.py:353
|
||||||
msgid "can be used as a proxy callback to deliver PGT"
|
msgid "can be used as a proxy callback to deliver PGT"
|
||||||
msgstr "peut être utilisé comme un callback pour recevoir un PGT"
|
msgstr "peut être utilisé comme un callback pour recevoir un PGT"
|
||||||
|
|
||||||
#: models.py:231
|
#: models.py:357
|
||||||
msgid "single log out"
|
msgid "single log out"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: models.py:232
|
#: models.py:358
|
||||||
msgid "Enable SLO for the service"
|
msgid "Enable SLO for the service"
|
||||||
msgstr "Active le SLO pour le service"
|
msgstr "Active le SLO pour le service"
|
||||||
|
|
||||||
#: models.py:239
|
#: models.py:365
|
||||||
msgid "single log out callback"
|
msgid "single log out callback"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: models.py:240
|
#: models.py:366
|
||||||
msgid ""
|
msgid ""
|
||||||
"URL where the SLO request will be POST. empty = service url\n"
|
"URL where the SLO request will be POST. empty = service url\n"
|
||||||
"This is usefull for non HTTP proxied services."
|
"This is usefull for non HTTP proxied services."
|
||||||
|
@ -167,63 +220,54 @@ msgstr ""
|
||||||
"service\n"
|
"service\n"
|
||||||
"Ceci n'est utilise que pour des services non HTTP proxifiés"
|
"Ceci n'est utilise que pour des services non HTTP proxifiés"
|
||||||
|
|
||||||
#: models.py:301
|
#: models.py:428
|
||||||
msgid "username"
|
msgid "username"
|
||||||
msgstr "nom d'utilisateur"
|
msgstr "nom d'utilisateur"
|
||||||
|
|
||||||
#: models.py:302
|
#: models.py:429
|
||||||
msgid "username allowed to connect to the service"
|
msgid "username allowed to connect to the service"
|
||||||
msgstr "noms d'utilisateurs autorisé à se connecter au service"
|
msgstr "noms d'utilisateurs autorisé à se connecter au service"
|
||||||
|
|
||||||
#: models.py:317
|
#: models.py:445
|
||||||
msgid "name of an attribut to send to the service, use * for all attributes"
|
msgid "name of an attribut to send to the service, use * for all attributes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"nom d'un attribut a envoyer au service, utiliser * pour tous les attributs"
|
"nom d'un attribut a envoyer au service, utiliser * pour tous les attributs"
|
||||||
|
|
||||||
#: models.py:322 models.py:368
|
#: models.py:450 models.py:498
|
||||||
msgid "replace"
|
msgid "replace"
|
||||||
msgstr "remplacement"
|
msgstr "remplacement"
|
||||||
|
|
||||||
#: models.py:323
|
#: models.py:451
|
||||||
msgid ""
|
msgid ""
|
||||||
"name under which the attribut will be showto the service. empty = default "
|
"name under which the attribut will be showto the service. empty = default "
|
||||||
"name of the attribut"
|
"name of the attribut"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"nom sous lequel l'attribut sera rendu visible au service. vide = inchangé"
|
"nom sous lequel l'attribut sera rendu visible au service. vide = inchangé"
|
||||||
|
|
||||||
#: models.py:339 models.py:357
|
#: models.py:468 models.py:487
|
||||||
msgid "attribut"
|
msgid "attribut"
|
||||||
msgstr "attribut"
|
msgstr "attribut"
|
||||||
|
|
||||||
#: models.py:340
|
#: models.py:469
|
||||||
msgid "Name of the attribut which must verify pattern"
|
msgid "Name of the attribut which must verify pattern"
|
||||||
msgstr "Nom de l'attribut devant vérifier un motif"
|
msgstr "Nom de l'attribut devant vérifier un motif"
|
||||||
|
|
||||||
#: models.py:345
|
#: models.py:474
|
||||||
msgid "a regular expression"
|
msgid "a regular expression"
|
||||||
msgstr "une expression régulière"
|
msgstr "une expression régulière"
|
||||||
|
|
||||||
#: models.py:358
|
#: models.py:488
|
||||||
msgid "Name of the attribut for which the value must be replace"
|
msgid "Name of the attribut for which the value must be replace"
|
||||||
msgstr "nom de l'attribue pour lequel la valeur doit être remplacé"
|
msgstr "nom de l'attribue pour lequel la valeur doit être remplacé"
|
||||||
|
|
||||||
#: models.py:363
|
#: models.py:493
|
||||||
msgid "An regular expression maching whats need to be replaced"
|
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:369
|
#: models.py:499
|
||||||
msgid "replace expression, groups are capture by \\1, \\2 …"
|
msgid "replace expression, groups are capture by \\1, \\2 …"
|
||||||
msgstr "expression de remplacement, les groupe sont capturé par \\1, \\2"
|
msgstr "expression de remplacement, les groupe sont capturé par \\1, \\2"
|
||||||
|
|
||||||
#: models.py:476
|
|
||||||
#, python-format
|
|
||||||
msgid ""
|
|
||||||
"Error during service logout %(service)s:\n"
|
|
||||||
"%(error)s"
|
|
||||||
msgstr ""
|
|
||||||
"Une erreur est survenue durant la déconnexion du service %(service)s:"
|
|
||||||
"%(error)s"
|
|
||||||
|
|
||||||
#: templates/cas_server/logged.html:6
|
#: templates/cas_server/logged.html:6
|
||||||
msgid "Logged"
|
msgid "Logged"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -252,7 +296,7 @@ msgstr "Connexion"
|
||||||
msgid "Connect to the service"
|
msgid "Connect to the service"
|
||||||
msgstr "Se connecter au service"
|
msgstr "Se connecter au service"
|
||||||
|
|
||||||
#: views.py:140
|
#: views.py:152
|
||||||
msgid ""
|
msgid ""
|
||||||
"<h3>Logout successful</h3>You have successfully logged out from the Central "
|
"<h3>Logout successful</h3>You have successfully logged out from the Central "
|
||||||
"Authentication Service. For security reasons, exit your web browser."
|
"Authentication Service. For security reasons, exit your web browser."
|
||||||
|
@ -261,7 +305,7 @@ msgstr ""
|
||||||
"d'Authentification. Pour des raisons de sécurité, veuillez fermer votre "
|
"d'Authentification. Pour des raisons de sécurité, veuillez fermer votre "
|
||||||
"navigateur."
|
"navigateur."
|
||||||
|
|
||||||
#: views.py:146
|
#: views.py:158
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"<h3>Logout successful</h3>You have successfully logged out from %s sessions "
|
"<h3>Logout successful</h3>You have successfully logged out from %s sessions "
|
||||||
|
@ -272,7 +316,7 @@ msgstr ""
|
||||||
"Service Central d'Authentification. Pour des raisons de sécurité, veuillez "
|
"Service Central d'Authentification. Pour des raisons de sécurité, veuillez "
|
||||||
"fermer votre navigateur."
|
"fermer votre navigateur."
|
||||||
|
|
||||||
#: views.py:153
|
#: views.py:165
|
||||||
msgid ""
|
msgid ""
|
||||||
"<h3>Logout successful</h3>You were already logged out from the Central "
|
"<h3>Logout successful</h3>You were already logged out from the Central "
|
||||||
"Authentication Service. For security reasons, exit your web browser."
|
"Authentication Service. For security reasons, exit your web browser."
|
||||||
|
@ -281,50 +325,57 @@ msgstr ""
|
||||||
"d'Authentification. Pour des raisons de sécurité, veuillez fermer votre "
|
"d'Authentification. Pour des raisons de sécurité, veuillez fermer votre "
|
||||||
"navigateur."
|
"navigateur."
|
||||||
|
|
||||||
#: views.py:294
|
#: views.py:349
|
||||||
msgid "Invalid login ticket"
|
msgid "Invalid login ticket"
|
||||||
msgstr "Ticket de connexion invalide, merci de réessayé de vous connecter"
|
msgstr "Ticket de connexion invalide, merci de réessayé de vous connecter"
|
||||||
|
|
||||||
#: views.py:410
|
#: views.py:470
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Authentication has been required by service %(name)s (%(url)s)"
|
msgid "Authentication has been required by service %(name)s (%(url)s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Une demande d'authentification a été émise pour le service %(name)s "
|
"Une demande d'authentification a été émise pour le service %(name)s "
|
||||||
"(%(url)s)."
|
"(%(url)s)."
|
||||||
|
|
||||||
#: views.py:448
|
#: views.py:508
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Service %(url)s non allowed."
|
msgid "Service %(url)s non allowed."
|
||||||
msgstr "le service %(url)s n'est pas autorisé."
|
msgstr "le service %(url)s n'est pas autorisé."
|
||||||
|
|
||||||
#: views.py:455
|
#: views.py:515
|
||||||
msgid "Username non allowed"
|
msgid "Username non allowed"
|
||||||
msgstr "Nom d'utilisateur non authorisé"
|
msgstr "Nom d'utilisateur non authorisé"
|
||||||
|
|
||||||
#: views.py:462
|
#: views.py:522
|
||||||
msgid "User charateristics non allowed"
|
msgid "User charateristics non allowed"
|
||||||
msgstr "Caractéristique utilisateur non autorisée"
|
msgstr "Caractéristique utilisateur non autorisée"
|
||||||
|
|
||||||
#: views.py:469
|
#: views.py:529
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "The attribut %(field)s is needed to use that service"
|
msgid "The attribut %(field)s is needed to use that service"
|
||||||
msgstr "L'attribut %(field)s est nécessaire pour se connecter à ce service"
|
msgstr "L'attribut %(field)s est nécessaire pour se connecter à ce service"
|
||||||
|
|
||||||
#: views.py:539
|
#: views.py:599
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Authentication renewal required by service %(name)s (%(url)s)."
|
msgid "Authentication renewal required by service %(name)s (%(url)s)."
|
||||||
msgstr "Demande de réauthentification pour le service %(name)s (%(url)s)."
|
msgstr "Demande de réauthentification pour le service %(name)s (%(url)s)."
|
||||||
|
|
||||||
#: views.py:546
|
#: views.py:606
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Authentication required by service %(name)s (%(url)s)."
|
msgid "Authentication required by service %(name)s (%(url)s)."
|
||||||
msgstr "Authentification requise par le service %(name)s (%(url)s)."
|
msgstr "Authentification requise par le service %(name)s (%(url)s)."
|
||||||
|
|
||||||
#: views.py:553
|
#: views.py:613
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Service %s non allowed"
|
msgid "Service %s non allowed"
|
||||||
msgstr "Le service %s n'est pas autorisé"
|
msgstr "Le service %s n'est pas autorisé"
|
||||||
|
|
||||||
|
#~ msgid ""
|
||||||
|
#~ "Error during service logout %(service)s:\n"
|
||||||
|
#~ "%(error)s"
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "Une erreur est survenue durant la déconnexion du service %(service)s:"
|
||||||
|
#~ "%(error)s"
|
||||||
|
|
||||||
#~ msgid "Successfully logout"
|
#~ msgid "Successfully logout"
|
||||||
#~ msgstr ""
|
#~ msgstr ""
|
||||||
#~ "<h3>Déconnexion réussie</h3>\n"
|
#~ "<h3>Déconnexion réussie</h3>\n"
|
||||||
|
|
50
cas_server/migrations/0007_auto_20160704_1510.py
Normal file
50
cas_server/migrations/0007_auto_20160704_1510.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.7 on 2016-07-04 15:10
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('cas_server', '0006_auto_20160623_1516'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FederatedIendityProvider',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('suffix', models.CharField(help_text='Suffix append to backend CAS returner username: `returned_username`@`suffix`', max_length=30, unique=True, verbose_name='suffix')),
|
||||||
|
('server_url', models.CharField(max_length=255, verbose_name='server url')),
|
||||||
|
('cas_protocol_version', models.CharField(choices=[(b'1', b'CAS 1.0'), (b'2', b'CAS 2.0'), (b'3', b'CAS 3.0'), (b'CAS_2_SAML_1_0', b'SAML 1.1')], default=b'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')),
|
||||||
|
('verbose_name', models.CharField(help_text='Name for this identity provider displayed on the login page', max_length=255, verbose_name='verbose name')),
|
||||||
|
('pos', models.IntegerField(default=100, help_text='Identity provider are sorted using the (position, verbose name, suffix) attributes', verbose_name='position')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'identity provider',
|
||||||
|
'verbose_name_plural': 'identity providers',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='federateduser',
|
||||||
|
name='provider',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cas_server.FederatedIendityProvider'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='federateslo',
|
||||||
|
name='ticket',
|
||||||
|
field=models.CharField(db_index=True, max_length=255),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='servicepattern',
|
||||||
|
name='pos',
|
||||||
|
field=models.IntegerField(default=100, help_text='service patterns are sorted using the position attribute', verbose_name='position'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='federateslo',
|
||||||
|
unique_together=set([('username', 'session_key', 'ticket')]),
|
||||||
|
),
|
||||||
|
]
|
|
@ -17,6 +17,7 @@ from django.db.models import Q
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from picklefield.fields import PickledObjectField
|
from picklefield.fields import PickledObjectField
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -34,18 +35,93 @@ SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
|
class FederatedIendityProvider(models.Model):
|
||||||
|
"""An identity provider for the federated mode"""
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("identity provider")
|
||||||
|
verbose_name_plural = _("identity providers")
|
||||||
|
suffix = models.CharField(
|
||||||
|
max_length=30,
|
||||||
|
unique=True,
|
||||||
|
verbose_name=_(u"suffix"),
|
||||||
|
help_text=_("Suffix append to backend CAS returner username: `returned_username`@`suffix`")
|
||||||
|
)
|
||||||
|
server_url = models.CharField(max_length=255, verbose_name=_(u"server url"))
|
||||||
|
cas_protocol_version = models.CharField(
|
||||||
|
max_length=30,
|
||||||
|
choices=[
|
||||||
|
("1", "CAS 1.0"),
|
||||||
|
("2", "CAS 2.0"),
|
||||||
|
("3", "CAS 3.0"),
|
||||||
|
("CAS_2_SAML_1_0", "SAML 1.1")
|
||||||
|
],
|
||||||
|
verbose_name=_(u"CAS protocol version"),
|
||||||
|
help_text=_("Version of the CAS protocol to use when sending requests the the backend CAS"),
|
||||||
|
default="3"
|
||||||
|
)
|
||||||
|
verbose_name = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=_(u"verbose name"),
|
||||||
|
help_text=_("Name for this identity provider displayed on the login page")
|
||||||
|
)
|
||||||
|
pos = models.IntegerField(
|
||||||
|
default=100,
|
||||||
|
verbose_name=_(u"position"),
|
||||||
|
help_text=_(
|
||||||
|
(
|
||||||
|
u"Identity provider are sorted using the "
|
||||||
|
u"(position, verbose name, suffix) attributes"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.verbose_name
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_username_from_suffix(username, suffix):
|
||||||
|
"""Transform backend username into federated username using `suffix`"""
|
||||||
|
return u'%s@%s' % (username, suffix)
|
||||||
|
|
||||||
|
def build_username(self, username):
|
||||||
|
"""Transform backend username into federated username"""
|
||||||
|
return u'%s@%s' % (username, self.suffix)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class FederatedUser(models.Model):
|
class FederatedUser(models.Model):
|
||||||
"""A federated user as returner by a CAS provider (username and attributes)"""
|
"""A federated user as returner by a CAS provider (username and attributes)"""
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("username", "provider")
|
unique_together = ("username", "provider")
|
||||||
username = models.CharField(max_length=124)
|
username = models.CharField(max_length=124)
|
||||||
provider = models.CharField(max_length=124)
|
provider = models.ForeignKey(FederatedIendityProvider, on_delete=models.CASCADE)
|
||||||
attributs = PickledObjectField()
|
attributs = PickledObjectField()
|
||||||
ticket = models.CharField(max_length=255)
|
ticket = models.CharField(max_length=255)
|
||||||
last_update = models.DateTimeField(auto_now=True)
|
last_update = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return u"%s@%s" % (self.username, self.provider)
|
return self.federated_username
|
||||||
|
|
||||||
|
@property
|
||||||
|
def federated_username(self):
|
||||||
|
"""return the federated username with a suffix"""
|
||||||
|
return self.provider.build_username(self.username)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_from_federated_username(cls, username):
|
||||||
|
"""return a FederatedUser object from a federated username"""
|
||||||
|
if username is None:
|
||||||
|
raise cls.DoesNotExist()
|
||||||
|
else:
|
||||||
|
component = username.split('@')
|
||||||
|
username = '@'.join(component[:-1])
|
||||||
|
suffix = component[-1]
|
||||||
|
try:
|
||||||
|
provider = FederatedIendityProvider.objects.get(suffix=suffix)
|
||||||
|
return cls.objects.get(username=username, provider=provider)
|
||||||
|
except FederatedIendityProvider.DoesNotExist:
|
||||||
|
raise cls.DoesNotExist()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def clean_old_entries(cls):
|
def clean_old_entries(cls):
|
||||||
|
@ -55,17 +131,17 @@ class FederatedUser(models.Model):
|
||||||
)
|
)
|
||||||
known_users = {user.username for user in User.objects.all()}
|
known_users = {user.username for user in User.objects.all()}
|
||||||
for user in federated_users:
|
for user in federated_users:
|
||||||
if not ('%s@%s' % (user.username, user.provider)) in known_users:
|
if user.federated_username not in known_users:
|
||||||
user.delete()
|
user.delete()
|
||||||
|
|
||||||
|
|
||||||
class FederateSLO(models.Model):
|
class FederateSLO(models.Model):
|
||||||
"""An association between a CAS provider ticket and a (username, session) for processing SLO"""
|
"""An association between a CAS provider ticket and a (username, session) for processing SLO"""
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("username", "session_key")
|
unique_together = ("username", "session_key", "ticket")
|
||||||
username = models.CharField(max_length=30)
|
username = models.CharField(max_length=30)
|
||||||
session_key = models.CharField(max_length=40, blank=True, null=True)
|
session_key = models.CharField(max_length=40, blank=True, null=True)
|
||||||
ticket = models.CharField(max_length=255)
|
ticket = models.CharField(max_length=255, db_index=True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def clean_deleted_sessions(cls):
|
def clean_deleted_sessions(cls):
|
||||||
|
@ -75,6 +151,7 @@ class FederateSLO(models.Model):
|
||||||
federate_slo.delete()
|
federate_slo.delete()
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class User(models.Model):
|
class User(models.Model):
|
||||||
"""A user logged into the CAS"""
|
"""A user logged into the CAS"""
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -117,7 +194,7 @@ class User(models.Model):
|
||||||
"""return a fresh dict for the user attributs"""
|
"""return a fresh dict for the user attributs"""
|
||||||
return utils.import_attr(settings.CAS_AUTH_CLASS)(self.username).attributs()
|
return utils.import_attr(settings.CAS_AUTH_CLASS)(self.username).attributs()
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return u"%s - %s" % (self.username, self.session_key)
|
return u"%s - %s" % (self.username, self.session_key)
|
||||||
|
|
||||||
def logout(self, request=None):
|
def logout(self, request=None):
|
||||||
|
@ -222,6 +299,7 @@ class UserFieldNotDefined(ServicePatternException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class ServicePattern(models.Model):
|
class ServicePattern(models.Model):
|
||||||
"""Allowed services pattern agains services are tested to"""
|
"""Allowed services pattern agains services are tested to"""
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -231,7 +309,8 @@ class ServicePattern(models.Model):
|
||||||
|
|
||||||
pos = models.IntegerField(
|
pos = models.IntegerField(
|
||||||
default=100,
|
default=100,
|
||||||
verbose_name=_(u"position")
|
verbose_name=_(u"position"),
|
||||||
|
help_text=_(u"service patterns are sorted using the position attribute")
|
||||||
)
|
)
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
|
@ -288,7 +367,7 @@ class ServicePattern(models.Model):
|
||||||
u"This is usefull for non HTTP proxied services.")
|
u"This is usefull for non HTTP proxied services.")
|
||||||
)
|
)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return u"%s: %s" % (self.pos, self.pattern)
|
return u"%s: %s" % (self.pos, self.pattern)
|
||||||
|
|
||||||
def check_user(self, user):
|
def check_user(self, user):
|
||||||
|
@ -341,6 +420,7 @@ class ServicePattern(models.Model):
|
||||||
raise cls.DoesNotExist()
|
raise cls.DoesNotExist()
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Username(models.Model):
|
class Username(models.Model):
|
||||||
"""A list of allowed usernames on a service pattern"""
|
"""A list of allowed usernames on a service pattern"""
|
||||||
value = models.CharField(
|
value = models.CharField(
|
||||||
|
@ -350,10 +430,11 @@ class Username(models.Model):
|
||||||
)
|
)
|
||||||
service_pattern = models.ForeignKey(ServicePattern, related_name="usernames")
|
service_pattern = models.ForeignKey(ServicePattern, related_name="usernames")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class ReplaceAttributName(models.Model):
|
class ReplaceAttributName(models.Model):
|
||||||
"""A list of replacement of attributs name for a service pattern"""
|
"""A list of replacement of attributs name for a service pattern"""
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -372,13 +453,14 @@ class ReplaceAttributName(models.Model):
|
||||||
)
|
)
|
||||||
service_pattern = models.ForeignKey(ServicePattern, related_name="attributs")
|
service_pattern = models.ForeignKey(ServicePattern, related_name="attributs")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
if not self.replace:
|
if not self.replace:
|
||||||
return self.name
|
return self.name
|
||||||
else:
|
else:
|
||||||
return u"%s → %s" % (self.name, self.replace)
|
return u"%s → %s" % (self.name, self.replace)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class FilterAttributValue(models.Model):
|
class FilterAttributValue(models.Model):
|
||||||
"""A list of filter on attributs for a service pattern"""
|
"""A list of filter on attributs for a service pattern"""
|
||||||
attribut = models.CharField(
|
attribut = models.CharField(
|
||||||
|
@ -393,10 +475,11 @@ class FilterAttributValue(models.Model):
|
||||||
)
|
)
|
||||||
service_pattern = models.ForeignKey(ServicePattern, related_name="filters")
|
service_pattern = models.ForeignKey(ServicePattern, related_name="filters")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return u"%s %s" % (self.attribut, self.pattern)
|
return u"%s %s" % (self.attribut, self.pattern)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class ReplaceAttributValue(models.Model):
|
class ReplaceAttributValue(models.Model):
|
||||||
"""Replacement to apply on attributs values for a service pattern"""
|
"""Replacement to apply on attributs values for a service pattern"""
|
||||||
attribut = models.CharField(
|
attribut = models.CharField(
|
||||||
|
@ -417,10 +500,11 @@ class ReplaceAttributValue(models.Model):
|
||||||
)
|
)
|
||||||
service_pattern = models.ForeignKey(ServicePattern, related_name="replacements")
|
service_pattern = models.ForeignKey(ServicePattern, related_name="replacements")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return u"%s %s %s" % (self.attribut, self.pattern, self.replace)
|
return u"%s %s %s" % (self.attribut, self.pattern, self.replace)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Ticket(models.Model):
|
class Ticket(models.Model):
|
||||||
"""Generic class for a Ticket"""
|
"""Generic class for a Ticket"""
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -437,7 +521,7 @@ class Ticket(models.Model):
|
||||||
VALIDITY = settings.CAS_TICKET_VALIDITY
|
VALIDITY = settings.CAS_TICKET_VALIDITY
|
||||||
TIMEOUT = settings.CAS_TICKET_TIMEOUT
|
TIMEOUT = settings.CAS_TICKET_TIMEOUT
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return u"Ticket-%s" % self.pk
|
return u"Ticket-%s" % self.pk
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -507,34 +591,38 @@ class Ticket(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class ServiceTicket(Ticket):
|
class ServiceTicket(Ticket):
|
||||||
"""A Service Ticket"""
|
"""A Service Ticket"""
|
||||||
PREFIX = settings.CAS_SERVICE_TICKET_PREFIX
|
PREFIX = settings.CAS_SERVICE_TICKET_PREFIX
|
||||||
value = models.CharField(max_length=255, default=utils.gen_st, unique=True)
|
value = models.CharField(max_length=255, default=utils.gen_st, unique=True)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return u"ServiceTicket-%s" % self.pk
|
return u"ServiceTicket-%s" % self.pk
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class ProxyTicket(Ticket):
|
class ProxyTicket(Ticket):
|
||||||
"""A Proxy Ticket"""
|
"""A Proxy Ticket"""
|
||||||
PREFIX = settings.CAS_PROXY_TICKET_PREFIX
|
PREFIX = settings.CAS_PROXY_TICKET_PREFIX
|
||||||
value = models.CharField(max_length=255, default=utils.gen_pt, unique=True)
|
value = models.CharField(max_length=255, default=utils.gen_pt, unique=True)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return u"ProxyTicket-%s" % self.pk
|
return u"ProxyTicket-%s" % self.pk
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class ProxyGrantingTicket(Ticket):
|
class ProxyGrantingTicket(Ticket):
|
||||||
"""A Proxy Granting Ticket"""
|
"""A Proxy Granting Ticket"""
|
||||||
PREFIX = settings.CAS_PROXY_GRANTING_TICKET_PREFIX
|
PREFIX = settings.CAS_PROXY_GRANTING_TICKET_PREFIX
|
||||||
VALIDITY = settings.CAS_PGT_VALIDITY
|
VALIDITY = settings.CAS_PGT_VALIDITY
|
||||||
value = models.CharField(max_length=255, default=utils.gen_pgt, unique=True)
|
value = models.CharField(max_length=255, default=utils.gen_pgt, unique=True)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return u"ProxyGrantingTicket-%s" % self.pk
|
return u"ProxyGrantingTicket-%s" % self.pk
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Proxy(models.Model):
|
class Proxy(models.Model):
|
||||||
"""A list of proxies on `ProxyTicket`"""
|
"""A list of proxies on `ProxyTicket`"""
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -542,5 +630,5 @@ class Proxy(models.Model):
|
||||||
url = models.CharField(max_length=255)
|
url = models.CharField(max_length=255)
|
||||||
proxy_ticket = models.ForeignKey(ProxyTicket, related_name="proxies")
|
proxy_ticket = models.ForeignKey(ProxyTicket, related_name="proxies")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.url
|
return self.url
|
||||||
|
|
|
@ -23,27 +23,28 @@ from cas_server.tests.utils import get_auth_client
|
||||||
|
|
||||||
class BaseServicePattern(object):
|
class BaseServicePattern(object):
|
||||||
"""Mixing for setting up service pattern for testing"""
|
"""Mixing for setting up service pattern for testing"""
|
||||||
def setup_service_patterns(self, proxy=False):
|
@classmethod
|
||||||
|
def setup_service_patterns(cls, proxy=False):
|
||||||
"""setting up service pattern"""
|
"""setting up service pattern"""
|
||||||
# For general purpose testing
|
# For general purpose testing
|
||||||
self.service = "https://www.example.com"
|
cls.service = "https://www.example.com"
|
||||||
self.service_pattern = models.ServicePattern.objects.create(
|
cls.service_pattern = models.ServicePattern.objects.create(
|
||||||
name="example",
|
name="example",
|
||||||
pattern="^https://www\.example\.com(/.*)?$",
|
pattern="^https://www\.example\.com(/.*)?$",
|
||||||
proxy=proxy,
|
proxy=proxy,
|
||||||
)
|
)
|
||||||
models.ReplaceAttributName.objects.create(name="*", service_pattern=self.service_pattern)
|
models.ReplaceAttributName.objects.create(name="*", service_pattern=cls.service_pattern)
|
||||||
|
|
||||||
# For testing the restrict_users attributes
|
# For testing the restrict_users attributes
|
||||||
self.service_restrict_user_fail = "https://restrict_user_fail.example.com"
|
cls.service_restrict_user_fail = "https://restrict_user_fail.example.com"
|
||||||
self.service_pattern_restrict_user_fail = models.ServicePattern.objects.create(
|
cls.service_pattern_restrict_user_fail = models.ServicePattern.objects.create(
|
||||||
name="restrict_user_fail",
|
name="restrict_user_fail",
|
||||||
pattern="^https://restrict_user_fail\.example\.com(/.*)?$",
|
pattern="^https://restrict_user_fail\.example\.com(/.*)?$",
|
||||||
restrict_users=True,
|
restrict_users=True,
|
||||||
proxy=proxy,
|
proxy=proxy,
|
||||||
)
|
)
|
||||||
self.service_restrict_user_success = "https://restrict_user_success.example.com"
|
cls.service_restrict_user_success = "https://restrict_user_success.example.com"
|
||||||
self.service_pattern_restrict_user_success = models.ServicePattern.objects.create(
|
cls.service_pattern_restrict_user_success = models.ServicePattern.objects.create(
|
||||||
name="restrict_user_success",
|
name="restrict_user_success",
|
||||||
pattern="^https://restrict_user_success\.example\.com(/.*)?$",
|
pattern="^https://restrict_user_success\.example\.com(/.*)?$",
|
||||||
restrict_users=True,
|
restrict_users=True,
|
||||||
|
@ -51,12 +52,12 @@ class BaseServicePattern(object):
|
||||||
)
|
)
|
||||||
models.Username.objects.create(
|
models.Username.objects.create(
|
||||||
value=settings.CAS_TEST_USER,
|
value=settings.CAS_TEST_USER,
|
||||||
service_pattern=self.service_pattern_restrict_user_success
|
service_pattern=cls.service_pattern_restrict_user_success
|
||||||
)
|
)
|
||||||
|
|
||||||
# For testing the user attributes filtering conditions
|
# For testing the user attributes filtering conditions
|
||||||
self.service_filter_fail = "https://filter_fail.example.com"
|
cls.service_filter_fail = "https://filter_fail.example.com"
|
||||||
self.service_pattern_filter_fail = models.ServicePattern.objects.create(
|
cls.service_pattern_filter_fail = models.ServicePattern.objects.create(
|
||||||
name="filter_fail",
|
name="filter_fail",
|
||||||
pattern="^https://filter_fail\.example\.com(/.*)?$",
|
pattern="^https://filter_fail\.example\.com(/.*)?$",
|
||||||
proxy=proxy,
|
proxy=proxy,
|
||||||
|
@ -64,10 +65,10 @@ class BaseServicePattern(object):
|
||||||
models.FilterAttributValue.objects.create(
|
models.FilterAttributValue.objects.create(
|
||||||
attribut="right",
|
attribut="right",
|
||||||
pattern="^admin$",
|
pattern="^admin$",
|
||||||
service_pattern=self.service_pattern_filter_fail
|
service_pattern=cls.service_pattern_filter_fail
|
||||||
)
|
)
|
||||||
self.service_filter_fail_alt = "https://filter_fail_alt.example.com"
|
cls.service_filter_fail_alt = "https://filter_fail_alt.example.com"
|
||||||
self.service_pattern_filter_fail_alt = models.ServicePattern.objects.create(
|
cls.service_pattern_filter_fail_alt = models.ServicePattern.objects.create(
|
||||||
name="filter_fail_alt",
|
name="filter_fail_alt",
|
||||||
pattern="^https://filter_fail_alt\.example\.com(/.*)?$",
|
pattern="^https://filter_fail_alt\.example\.com(/.*)?$",
|
||||||
proxy=proxy,
|
proxy=proxy,
|
||||||
|
@ -75,10 +76,10 @@ class BaseServicePattern(object):
|
||||||
models.FilterAttributValue.objects.create(
|
models.FilterAttributValue.objects.create(
|
||||||
attribut="nom",
|
attribut="nom",
|
||||||
pattern="^toto$",
|
pattern="^toto$",
|
||||||
service_pattern=self.service_pattern_filter_fail_alt
|
service_pattern=cls.service_pattern_filter_fail_alt
|
||||||
)
|
)
|
||||||
self.service_filter_success = "https://filter_success.example.com"
|
cls.service_filter_success = "https://filter_success.example.com"
|
||||||
self.service_pattern_filter_success = models.ServicePattern.objects.create(
|
cls.service_pattern_filter_success = models.ServicePattern.objects.create(
|
||||||
name="filter_success",
|
name="filter_success",
|
||||||
pattern="^https://filter_success\.example\.com(/.*)?$",
|
pattern="^https://filter_success\.example\.com(/.*)?$",
|
||||||
proxy=proxy,
|
proxy=proxy,
|
||||||
|
@ -86,26 +87,26 @@ class BaseServicePattern(object):
|
||||||
models.FilterAttributValue.objects.create(
|
models.FilterAttributValue.objects.create(
|
||||||
attribut="email",
|
attribut="email",
|
||||||
pattern="^%s$" % re.escape(settings.CAS_TEST_ATTRIBUTES['email']),
|
pattern="^%s$" % re.escape(settings.CAS_TEST_ATTRIBUTES['email']),
|
||||||
service_pattern=self.service_pattern_filter_success
|
service_pattern=cls.service_pattern_filter_success
|
||||||
)
|
)
|
||||||
|
|
||||||
# For testing the user_field attributes
|
# For testing the user_field attributes
|
||||||
self.service_field_needed_fail = "https://field_needed_fail.example.com"
|
cls.service_field_needed_fail = "https://field_needed_fail.example.com"
|
||||||
self.service_pattern_field_needed_fail = models.ServicePattern.objects.create(
|
cls.service_pattern_field_needed_fail = models.ServicePattern.objects.create(
|
||||||
name="field_needed_fail",
|
name="field_needed_fail",
|
||||||
pattern="^https://field_needed_fail\.example\.com(/.*)?$",
|
pattern="^https://field_needed_fail\.example\.com(/.*)?$",
|
||||||
user_field="uid",
|
user_field="uid",
|
||||||
proxy=proxy,
|
proxy=proxy,
|
||||||
)
|
)
|
||||||
self.service_field_needed_success = "https://field_needed_success.example.com"
|
cls.service_field_needed_success = "https://field_needed_success.example.com"
|
||||||
self.service_pattern_field_needed_success = models.ServicePattern.objects.create(
|
cls.service_pattern_field_needed_success = models.ServicePattern.objects.create(
|
||||||
name="field_needed_success",
|
name="field_needed_success",
|
||||||
pattern="^https://field_needed_success\.example\.com(/.*)?$",
|
pattern="^https://field_needed_success\.example\.com(/.*)?$",
|
||||||
user_field="alias",
|
user_field="alias",
|
||||||
proxy=proxy,
|
proxy=proxy,
|
||||||
)
|
)
|
||||||
self.service_field_needed_success_alt = "https://field_needed_success_alt.example.com"
|
cls.service_field_needed_success_alt = "https://field_needed_success_alt.example.com"
|
||||||
self.service_pattern_field_needed_success = models.ServicePattern.objects.create(
|
cls.service_pattern_field_needed_success = models.ServicePattern.objects.create(
|
||||||
name="field_needed_success_alt",
|
name="field_needed_success_alt",
|
||||||
pattern="^https://field_needed_success_alt\.example\.com(/.*)?$",
|
pattern="^https://field_needed_success_alt\.example\.com(/.*)?$",
|
||||||
user_field="nom",
|
user_field="nom",
|
||||||
|
@ -238,3 +239,17 @@ class CanLogin(object):
|
||||||
self.assertTrue(client.session.get("username") is None)
|
self.assertTrue(client.session.get("username") is None)
|
||||||
self.assertTrue(client.session.get("warn") is None)
|
self.assertTrue(client.session.get("warn") is None)
|
||||||
self.assertTrue(client.session.get("authenticated") is None)
|
self.assertTrue(client.session.get("authenticated") is None)
|
||||||
|
|
||||||
|
|
||||||
|
class FederatedIendityProviderModel(object):
|
||||||
|
"""Mixin for test classes using the FederatedIendityProvider model"""
|
||||||
|
@staticmethod
|
||||||
|
def setup_federated_identity_provider(providers):
|
||||||
|
"""setting up federated identity providers"""
|
||||||
|
for suffix, (server_url, cas_protocol_version, verbose_name) in providers.items():
|
||||||
|
models.FederatedIendityProvider.objects.create(
|
||||||
|
suffix=suffix,
|
||||||
|
server_url=server_url,
|
||||||
|
cas_protocol_version=cas_protocol_version,
|
||||||
|
verbose_name=verbose_name
|
||||||
|
)
|
||||||
|
|
|
@ -19,43 +19,37 @@ from django.test.utils import override_settings
|
||||||
|
|
||||||
from six.moves import reload_module
|
from six.moves import reload_module
|
||||||
|
|
||||||
from cas_server import utils, forms
|
from cas_server import utils, models
|
||||||
from cas_server.tests.mixin import BaseServicePattern, CanLogin
|
from cas_server.tests.mixin import BaseServicePattern, CanLogin, FederatedIendityProviderModel
|
||||||
from cas_server.tests import utils as tests_utils
|
from cas_server.tests import utils as tests_utils
|
||||||
|
|
||||||
PROVIDERS = {
|
PROVIDERS = {
|
||||||
"example.com": ("http://127.0.0.1:8080", 1, "Example dot com"),
|
"example.com": ("http://127.0.0.1:8080", '1', "Example dot com"),
|
||||||
"example.org": ("http://127.0.0.1:8081", 2, "Example dot org"),
|
"example.org": ("http://127.0.0.1:8081", '2', "Example dot org"),
|
||||||
"example.net": ("http://127.0.0.1:8082", 3, "Example dot net"),
|
"example.net": ("http://127.0.0.1:8082", '3', "Example dot net"),
|
||||||
"example.test": ("http://127.0.0.1:8083", 'CAS_2_SAML_1_0'),
|
"example.test": ("http://127.0.0.1:8083", 'CAS_2_SAML_1_0', 'Example fot test'),
|
||||||
}
|
}
|
||||||
|
|
||||||
PROVIDERS_LIST = list(PROVIDERS.keys())
|
|
||||||
PROVIDERS_LIST.sort()
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(
|
@override_settings(
|
||||||
CAS_FEDERATE=True,
|
CAS_FEDERATE=True,
|
||||||
CAS_FEDERATE_PROVIDERS=PROVIDERS,
|
|
||||||
CAS_FEDERATE_PROVIDERS_LIST=PROVIDERS_LIST,
|
|
||||||
CAS_AUTH_CLASS="cas_server.auth.CASFederateAuth",
|
CAS_AUTH_CLASS="cas_server.auth.CASFederateAuth",
|
||||||
# test with a non ascii username
|
# test with a non ascii username
|
||||||
CAS_TEST_USER=u"dédé"
|
CAS_TEST_USER=u"dédé"
|
||||||
)
|
)
|
||||||
class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
class FederateAuthLoginLogoutTestCase(
|
||||||
|
TestCase, BaseServicePattern, CanLogin, FederatedIendityProviderModel
|
||||||
|
):
|
||||||
"""tests for the views login logout and federate then the federated mode is enabled"""
|
"""tests for the views login logout and federate then the federated mode is enabled"""
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Prepare the test context"""
|
"""Prepare the test context"""
|
||||||
self.setup_service_patterns()
|
self.setup_service_patterns()
|
||||||
reload_module(forms)
|
self.setup_federated_identity_provider(PROVIDERS)
|
||||||
|
|
||||||
def test_default_settings(self):
|
def test_default_settings(self):
|
||||||
"""default settings should populated some default variable then CAS_FEDERATE is True"""
|
"""default settings should populated some default variable then CAS_FEDERATE is True"""
|
||||||
provider_list = settings.CAS_FEDERATE_PROVIDERS_LIST
|
|
||||||
del settings.CAS_FEDERATE_PROVIDERS_LIST
|
|
||||||
del settings.CAS_AUTH_CLASS
|
del settings.CAS_AUTH_CLASS
|
||||||
reload_module(default_settings)
|
reload_module(default_settings)
|
||||||
self.assertEqual(settings.CAS_FEDERATE_PROVIDERS_LIST, provider_list)
|
|
||||||
self.assertEqual(settings.CAS_AUTH_CLASS, "cas_server.auth.CASFederateAuth")
|
self.assertEqual(settings.CAS_AUTH_CLASS, "cas_server.auth.CASFederateAuth")
|
||||||
|
|
||||||
def test_login_get_provider(self):
|
def test_login_get_provider(self):
|
||||||
|
@ -63,10 +57,10 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
client = Client()
|
client = Client()
|
||||||
response = client.get("/login")
|
response = client.get("/login")
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
for key, value in settings.CAS_FEDERATE_PROVIDERS.items():
|
for provider in models.FederatedIendityProvider.objects.all():
|
||||||
self.assertTrue('<option value="%s">%s</option>' % (
|
self.assertTrue('<option value="%s">%s</option>' % (
|
||||||
key,
|
provider.suffix,
|
||||||
utils.get_tuple(value, 2, key)
|
provider.verbose_name
|
||||||
) in response.content.decode("utf-8"))
|
) in response.content.decode("utf-8"))
|
||||||
self.assertEqual(response.context['post_url'], '/federate')
|
self.assertEqual(response.context['post_url'], '/federate')
|
||||||
|
|
||||||
|
@ -74,10 +68,11 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
"""test a successful login wrokflow"""
|
"""test a successful login wrokflow"""
|
||||||
tickets = []
|
tickets = []
|
||||||
# choose the example.com provider
|
# choose the example.com provider
|
||||||
for (provider, cas_port) in [
|
for (suffix, cas_port) in [
|
||||||
("example.com", 8080), ("example.org", 8081),
|
("example.com", 8080), ("example.org", 8081),
|
||||||
("example.net", 8082), ("example.test", 8083)
|
("example.net", 8082), ("example.test", 8083)
|
||||||
]:
|
]:
|
||||||
|
provider = models.FederatedIendityProvider.objects.get(suffix=suffix)
|
||||||
# get a bare client
|
# get a bare client
|
||||||
client = Client()
|
client = Client()
|
||||||
# fetch the login page
|
# fetch the login page
|
||||||
|
@ -86,7 +81,7 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
self.assertEqual(response.context['post_url'], '/federate')
|
self.assertEqual(response.context['post_url'], '/federate')
|
||||||
# get current form parameter
|
# get current form parameter
|
||||||
params = tests_utils.copy_form(response.context["form"])
|
params = tests_utils.copy_form(response.context["form"])
|
||||||
params['provider'] = provider
|
params['provider'] = provider.suffix
|
||||||
if remember:
|
if remember:
|
||||||
params['remember'] = 'on'
|
params['remember'] = 'on'
|
||||||
# post the choosed provider
|
# post the choosed provider
|
||||||
|
@ -96,22 +91,22 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
if remember:
|
if remember:
|
||||||
self.assertEqual(response["Location"], '%s/federate/%s?remember=on' % (
|
self.assertEqual(response["Location"], '%s/federate/%s?remember=on' % (
|
||||||
'http://testserver' if django.VERSION < (1, 9) else "",
|
'http://testserver' if django.VERSION < (1, 9) else "",
|
||||||
provider
|
provider.suffix
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
self.assertEqual(response["Location"], '%s/federate/%s' % (
|
self.assertEqual(response["Location"], '%s/federate/%s' % (
|
||||||
'http://testserver' if django.VERSION < (1, 9) else "",
|
'http://testserver' if django.VERSION < (1, 9) else "",
|
||||||
provider
|
provider.suffix
|
||||||
))
|
))
|
||||||
# let's follow the redirect
|
# let's follow the redirect
|
||||||
response = client.get('/federate/%s' % provider)
|
response = client.get('/federate/%s' % provider.suffix)
|
||||||
# we are redirected to the provider CAS for authentication
|
# we are redirected to the provider CAS for authentication
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response["Location"],
|
response["Location"],
|
||||||
"%s/login?service=http%%3A%%2F%%2Ftestserver%%2Ffederate%%2F%s" % (
|
"%s/login?service=http%%3A%%2F%%2Ftestserver%%2Ffederate%%2F%s" % (
|
||||||
settings.CAS_FEDERATE_PROVIDERS[provider][0],
|
provider.server_url,
|
||||||
provider
|
provider.suffix
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# let's generate a ticket
|
# let's generate a ticket
|
||||||
|
@ -119,7 +114,7 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
# we lauch a dummy CAS server that only validate once for the service
|
# we lauch a dummy CAS server that only validate once for the service
|
||||||
# http://testserver/federate/example.com with `ticket`
|
# http://testserver/federate/example.com with `ticket`
|
||||||
tests_utils.DummyCAS.run(
|
tests_utils.DummyCAS.run(
|
||||||
("http://testserver/federate/%s" % provider).encode("ascii"),
|
("http://testserver/federate/%s" % provider.suffix).encode("ascii"),
|
||||||
ticket.encode("ascii"),
|
ticket.encode("ascii"),
|
||||||
settings.CAS_TEST_USER.encode("utf8"),
|
settings.CAS_TEST_USER.encode("utf8"),
|
||||||
[],
|
[],
|
||||||
|
@ -127,7 +122,7 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
)
|
)
|
||||||
# we normally provide a good ticket and should be redirected to /login as the ticket
|
# we normally provide a good ticket and should be redirected to /login as the ticket
|
||||||
# get successfully validated again the dummy CAS
|
# get successfully validated again the dummy CAS
|
||||||
response = client.get('/federate/%s' % provider, {'ticket': ticket})
|
response = client.get('/federate/%s' % provider.suffix, {'ticket': ticket})
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response["Location"], "%s/login" % (
|
self.assertEqual(response["Location"], "%s/login" % (
|
||||||
'http://testserver' if django.VERSION < (1, 9) else ""
|
'http://testserver' if django.VERSION < (1, 9) else ""
|
||||||
|
@ -143,7 +138,7 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
response = client.post("/login", params)
|
response = client.post("/login", params)
|
||||||
# the user should now being authenticated using username test@`provider`
|
# the user should now being authenticated using username test@`provider`
|
||||||
self.assert_logged(
|
self.assert_logged(
|
||||||
client, response, username='%s@%s' % (settings.CAS_TEST_USER, provider)
|
client, response, username=provider.build_username(settings.CAS_TEST_USER)
|
||||||
)
|
)
|
||||||
tickets.append((provider, ticket, client))
|
tickets.append((provider, ticket, client))
|
||||||
|
|
||||||
|
@ -198,7 +193,7 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response["Location"],
|
response["Location"],
|
||||||
"%s/login?service=http%%3A%%2F%%2Ftestserver%%2Ffederate%%2F%s" % (
|
"%s/login?service=http%%3A%%2F%%2Ftestserver%%2Ffederate%%2F%s" % (
|
||||||
settings.CAS_FEDERATE_PROVIDERS[good_provider][0],
|
models.FederatedIendityProvider.objects.get(suffix=good_provider).server_url,
|
||||||
good_provider
|
good_provider
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -216,7 +211,7 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response["Location"],
|
response["Location"],
|
||||||
"%s/login?service=http%%3A%%2F%%2Ftestserver%%2Ffederate%%2F%s" % (
|
"%s/login?service=http%%3A%%2F%%2Ftestserver%%2Ffederate%%2F%s" % (
|
||||||
settings.CAS_FEDERATE_PROVIDERS[good_provider][0],
|
models.FederatedIendityProvider.objects.get(suffix=good_provider).server_url,
|
||||||
good_provider
|
good_provider
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -234,45 +229,45 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
for (provider, ticket, client) in tickets:
|
for (provider, ticket, client) in tickets:
|
||||||
# SLO for an unkown ticket should do nothing
|
# SLO for an unkown ticket should do nothing
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/federate/%s" % provider,
|
"/federate/%s" % provider.suffix,
|
||||||
{'logoutRequest': tests_utils.logout_request(utils.gen_st())}
|
{'logoutRequest': tests_utils.logout_request(utils.gen_st())}
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(response.content, b"ok")
|
self.assertEqual(response.content, b"ok")
|
||||||
# Bad SLO format should do nothing
|
# Bad SLO format should do nothing
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/federate/%s" % provider,
|
"/federate/%s" % provider.suffix,
|
||||||
{'logoutRequest': ""}
|
{'logoutRequest': ""}
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(response.content, b"ok")
|
self.assertEqual(response.content, b"ok")
|
||||||
# Bad SLO format should do nothing
|
# Bad SLO format should do nothing
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/federate/%s" % provider,
|
"/federate/%s" % provider.suffix,
|
||||||
{'logoutRequest': "<root></root>"}
|
{'logoutRequest': "<root></root>"}
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(response.content, b"ok")
|
self.assertEqual(response.content, b"ok")
|
||||||
response = client.get("/login")
|
response = client.get("/login")
|
||||||
self.assert_logged(
|
self.assert_logged(
|
||||||
client, response, username='%s@%s' % (settings.CAS_TEST_USER, provider)
|
client, response, username=provider.build_username(settings.CAS_TEST_USER)
|
||||||
)
|
)
|
||||||
|
|
||||||
# SLO for a previously logged ticket should log out the user if CAS version is
|
# SLO for a previously logged ticket should log out the user if CAS version is
|
||||||
# 3 or 'CAS_2_SAML_1_0'
|
# 3 or 'CAS_2_SAML_1_0'
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/federate/%s" % provider,
|
"/federate/%s" % provider.suffix,
|
||||||
{'logoutRequest': tests_utils.logout_request(ticket)}
|
{'logoutRequest': tests_utils.logout_request(ticket)}
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(response.content, b"ok")
|
self.assertEqual(response.content, b"ok")
|
||||||
|
|
||||||
response = client.get("/login")
|
response = client.get("/login")
|
||||||
if settings.CAS_FEDERATE_PROVIDERS[provider][1] in {3, 'CAS_2_SAML_1_0'}: # support SLO
|
if provider.cas_protocol_version in {'3', 'CAS_2_SAML_1_0'}: # support SLO
|
||||||
self.assert_login_failed(client, response)
|
self.assert_login_failed(client, response)
|
||||||
else:
|
else:
|
||||||
self.assert_logged(
|
self.assert_logged(
|
||||||
client, response, username='%s@%s' % (settings.CAS_TEST_USER, provider)
|
client, response, username=provider.build_username(settings.CAS_TEST_USER)
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_federate_logout(self):
|
def test_federate_logout(self):
|
||||||
|
@ -287,7 +282,7 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response["Location"],
|
response["Location"],
|
||||||
"%s/logout" % settings.CAS_FEDERATE_PROVIDERS[provider][0]
|
"%s/logout" % provider.server_url,
|
||||||
)
|
)
|
||||||
response = client.get("/login")
|
response = client.get("/login")
|
||||||
self.assert_login_failed(client, response)
|
self.assert_login_failed(client, response)
|
||||||
|
@ -326,7 +321,7 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response["Location"], "%s/federate/%s" % (
|
self.assertEqual(response["Location"], "%s/federate/%s" % (
|
||||||
'http://testserver' if django.VERSION < (1, 9) else "",
|
'http://testserver' if django.VERSION < (1, 9) else "",
|
||||||
provider
|
provider.suffix
|
||||||
))
|
))
|
||||||
|
|
||||||
def test_login_bad_ticket(self):
|
def test_login_bad_ticket(self):
|
||||||
|
@ -338,7 +333,10 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
# get a bare client
|
# get a bare client
|
||||||
client = Client()
|
client = Client()
|
||||||
session = client.session
|
session = client.session
|
||||||
session["federate_username"] = '%s@%s' % (settings.CAS_TEST_USER, provider)
|
session["federate_username"] = models.FederatedIendityProvider.build_username_from_suffix(
|
||||||
|
settings.CAS_TEST_USER,
|
||||||
|
provider
|
||||||
|
)
|
||||||
session["federate_ticket"] = utils.gen_st()
|
session["federate_ticket"] = utils.gen_st()
|
||||||
if django.VERSION >= (1, 8):
|
if django.VERSION >= (1, 8):
|
||||||
session.save()
|
session.save()
|
||||||
|
@ -351,9 +349,12 @@ class FederateAuthLoginLogoutTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
# POST, as (username, ticket) are not valid, we should get the federate login page
|
# POST, as (username, ticket) are not valid, we should get the federate login page
|
||||||
response = client.post("/login", params)
|
response = client.post("/login", params)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
for key, value in settings.CAS_FEDERATE_PROVIDERS.items():
|
for provider in models.FederatedIendityProvider.objects.all():
|
||||||
self.assertTrue('<option value="%s">%s</option>' % (
|
self.assertIn(
|
||||||
key,
|
'<option value="%s">%s</option>' % (
|
||||||
utils.get_tuple(value, 2, key)
|
provider.suffix,
|
||||||
) in response.content.decode("utf-8"))
|
provider.verbose_name
|
||||||
|
),
|
||||||
|
response.content.decode("utf-8")
|
||||||
|
)
|
||||||
self.assertEqual(response.context['post_url'], '/federate')
|
self.assertEqual(response.context['post_url'], '/federate')
|
||||||
|
|
|
@ -22,32 +22,39 @@ from importlib import import_module
|
||||||
|
|
||||||
from cas_server import models, utils
|
from cas_server import models, utils
|
||||||
from cas_server.tests.utils import get_auth_client, HttpParamsHandler
|
from cas_server.tests.utils import get_auth_client, HttpParamsHandler
|
||||||
from cas_server.tests.mixin import UserModels, BaseServicePattern
|
from cas_server.tests.mixin import UserModels, BaseServicePattern, FederatedIendityProviderModel
|
||||||
|
from cas_server.tests.test_federate import PROVIDERS
|
||||||
|
|
||||||
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
|
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
|
||||||
|
|
||||||
|
|
||||||
class FederatedUserTestCase(TestCase, UserModels):
|
class FederatedUserTestCase(TestCase, UserModels, FederatedIendityProviderModel):
|
||||||
"""test for the federated user model"""
|
"""test for the federated user model"""
|
||||||
|
def setUp(self):
|
||||||
|
"""Prepare the test context"""
|
||||||
|
self.setup_federated_identity_provider(PROVIDERS)
|
||||||
|
|
||||||
def test_clean_old_entries(self):
|
def test_clean_old_entries(self):
|
||||||
"""tests for clean_old_entries that should delete federated user no longer used"""
|
"""tests for clean_old_entries that should delete federated user no longer used"""
|
||||||
client = Client()
|
client = Client()
|
||||||
client.get("/login")
|
client.get("/login")
|
||||||
|
provider = models.FederatedIendityProvider.objects.get(suffix="example.com")
|
||||||
models.FederatedUser.objects.create(
|
models.FederatedUser.objects.create(
|
||||||
username="test1", provider="example.com", attributs={}, ticket=""
|
username="test1", provider=provider, attributs={}, ticket=""
|
||||||
)
|
)
|
||||||
models.FederatedUser.objects.create(
|
models.FederatedUser.objects.create(
|
||||||
username="test2", provider="example.com", attributs={}, ticket=""
|
username="test2", provider=provider, attributs={}, ticket=""
|
||||||
)
|
)
|
||||||
models.FederatedUser.objects.all().update(
|
models.FederatedUser.objects.all().update(
|
||||||
last_update=(timezone.now() - timedelta(seconds=settings.CAS_TICKET_TIMEOUT + 10))
|
last_update=(timezone.now() - timedelta(seconds=settings.CAS_TICKET_TIMEOUT + 10))
|
||||||
)
|
)
|
||||||
models.FederatedUser.objects.create(
|
models.FederatedUser.objects.create(
|
||||||
username="test3", provider="example.com", attributs={}, ticket=""
|
username="test3", provider=provider, attributs={}, ticket=""
|
||||||
)
|
)
|
||||||
models.User.objects.create(
|
models.User.objects.create(
|
||||||
username="test1@example.com", session_key=client.session.session_key
|
username="test1@example.com", session_key=client.session.session_key
|
||||||
)
|
)
|
||||||
|
self.assertEqual(len(models.FederatedUser.objects.all()), 3)
|
||||||
models.FederatedUser.clean_old_entries()
|
models.FederatedUser.clean_old_entries()
|
||||||
self.assertEqual(len(models.FederatedUser.objects.all()), 2)
|
self.assertEqual(len(models.FederatedUser.objects.all()), 2)
|
||||||
with self.assertRaises(models.FederatedUser.DoesNotExist):
|
with self.assertRaises(models.FederatedUser.DoesNotExist):
|
||||||
|
|
|
@ -22,6 +22,7 @@ from django.utils import timezone
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
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
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
@ -37,7 +38,7 @@ import cas_server.models as models
|
||||||
|
|
||||||
from .utils import json_response
|
from .utils import json_response
|
||||||
from .models import ServiceTicket, ProxyTicket, ProxyGrantingTicket
|
from .models import ServiceTicket, ProxyTicket, ProxyGrantingTicket
|
||||||
from .models import ServicePattern
|
from .models import ServicePattern, FederatedIendityProvider, FederatedUser
|
||||||
from .federate import CASFederateValidateUser
|
from .federate import CASFederateValidateUser
|
||||||
|
|
||||||
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
|
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
|
||||||
|
@ -123,11 +124,12 @@ class LogoutView(View, LogoutMixin):
|
||||||
self.init_get(request)
|
self.init_get(request)
|
||||||
# if CAS federation mode is enable, bakup the provider before flushing the sessions
|
# if CAS federation mode is enable, bakup the provider before flushing the sessions
|
||||||
if settings.CAS_FEDERATE:
|
if settings.CAS_FEDERATE:
|
||||||
if "username" in self.request.session:
|
try:
|
||||||
component = self.request.session["username"].split('@')
|
user = FederatedUser.get_from_federated_username(
|
||||||
provider = component[-1]
|
self.request.session.get("username")
|
||||||
auth = CASFederateValidateUser(provider, service_url="")
|
)
|
||||||
else:
|
auth = CASFederateValidateUser(user.provider, service_url="")
|
||||||
|
except FederatedUser.DoesNotExist:
|
||||||
auth = None
|
auth = None
|
||||||
session_nb = self.logout(self.request.GET.get("all"))
|
session_nb = self.logout(self.request.GET.get("all"))
|
||||||
# if CAS federation mode is enable, redirect to user CAS logout page
|
# if CAS federation mode is enable, redirect to user CAS logout page
|
||||||
|
@ -135,8 +137,7 @@ class LogoutView(View, LogoutMixin):
|
||||||
if auth is not None:
|
if auth is not None:
|
||||||
params = utils.copy_params(request.GET)
|
params = utils.copy_params(request.GET)
|
||||||
url = auth.get_logout_url()
|
url = auth.get_logout_url()
|
||||||
if url:
|
return HttpResponseRedirect(utils.update_url(url, params))
|
||||||
return HttpResponseRedirect(utils.update_url(url, params))
|
|
||||||
# if service is set, redirect to service after logout
|
# if service is set, redirect to service after logout
|
||||||
if self.service:
|
if self.service:
|
||||||
list(messages.get_messages(request)) # clean messages before leaving the django app
|
list(messages.get_messages(request)) # clean messages before leaving the django app
|
||||||
|
@ -201,16 +202,16 @@ class FederateAuth(View):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_cas_client(request, provider):
|
def get_cas_client(request, provider):
|
||||||
"""return a CAS client object matching provider"""
|
"""return a CAS client object matching provider"""
|
||||||
if provider in settings.CAS_FEDERATE_PROVIDERS: # pragma: no branch (should always be true)
|
service_url = utils.get_current_url(request, {"ticket", "provider"})
|
||||||
service_url = utils.get_current_url(request, {"ticket", "provider"})
|
return CASFederateValidateUser(provider, service_url)
|
||||||
return CASFederateValidateUser(provider, service_url)
|
|
||||||
|
|
||||||
def post(self, request, provider=None):
|
def post(self, request, provider=None):
|
||||||
"""method called on POST request"""
|
"""method called on POST request"""
|
||||||
if not settings.CAS_FEDERATE:
|
if not settings.CAS_FEDERATE:
|
||||||
return redirect("cas_server:login")
|
return redirect("cas_server:login")
|
||||||
# POST with a provider, this is probably an SLO request
|
# POST with a provider, this is probably an SLO request
|
||||||
if provider in settings.CAS_FEDERATE_PROVIDERS:
|
try:
|
||||||
|
provider = FederatedIendityProvider.objects.get(suffix=provider)
|
||||||
auth = self.get_cas_client(request, provider)
|
auth = self.get_cas_client(request, provider)
|
||||||
try:
|
try:
|
||||||
auth.clean_sessions(request.POST['logoutRequest'])
|
auth.clean_sessions(request.POST['logoutRequest'])
|
||||||
|
@ -218,7 +219,7 @@ class FederateAuth(View):
|
||||||
pass
|
pass
|
||||||
return HttpResponse("ok")
|
return HttpResponse("ok")
|
||||||
# else, a User is trying to log in using an identity provider
|
# else, a User is trying to log in using an identity provider
|
||||||
else:
|
except FederatedIendityProvider.DoesNotExist:
|
||||||
# Manually checking for csrf to protect the code below
|
# Manually checking for csrf to protect the code below
|
||||||
reason = CsrfViewMiddleware().process_view(request, None, (), {})
|
reason = CsrfViewMiddleware().process_view(request, None, (), {})
|
||||||
if reason is not None: # pragma: no cover (csrf checks are disabled during tests)
|
if reason is not None: # pragma: no cover (csrf checks are disabled during tests)
|
||||||
|
@ -231,7 +232,7 @@ class FederateAuth(View):
|
||||||
)
|
)
|
||||||
url = utils.reverse_params(
|
url = utils.reverse_params(
|
||||||
"cas_server:federateAuth",
|
"cas_server:federateAuth",
|
||||||
kwargs=dict(provider=form.cleaned_data["provider"]),
|
kwargs=dict(provider=form.cleaned_data["provider"].suffix),
|
||||||
params=params
|
params=params
|
||||||
)
|
)
|
||||||
response = HttpResponseRedirect(url)
|
response = HttpResponseRedirect(url)
|
||||||
|
@ -240,7 +241,7 @@ class FederateAuth(View):
|
||||||
utils.set_cookie(
|
utils.set_cookie(
|
||||||
response,
|
response,
|
||||||
"_remember_provider",
|
"_remember_provider",
|
||||||
request.POST["provider"],
|
form.cleaned_data["provider"].suffix,
|
||||||
max_age
|
max_age
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
@ -251,23 +252,24 @@ class FederateAuth(View):
|
||||||
"""method called on GET request"""
|
"""method called on GET request"""
|
||||||
if not settings.CAS_FEDERATE:
|
if not settings.CAS_FEDERATE:
|
||||||
return redirect("cas_server:login")
|
return redirect("cas_server:login")
|
||||||
if provider not in settings.CAS_FEDERATE_PROVIDERS:
|
try:
|
||||||
return redirect("cas_server:login")
|
provider = FederatedIendityProvider.objects.get(suffix=provider)
|
||||||
auth = self.get_cas_client(request, provider)
|
auth = self.get_cas_client(request, provider)
|
||||||
if 'ticket' not in request.GET:
|
if 'ticket' not in request.GET:
|
||||||
return HttpResponseRedirect(auth.get_login_url())
|
|
||||||
else:
|
|
||||||
ticket = request.GET['ticket']
|
|
||||||
if auth.verify_ticket(ticket):
|
|
||||||
params = utils.copy_params(request.GET, ignore={"ticket"})
|
|
||||||
username = u"%s@%s" % (auth.username, auth.provider)
|
|
||||||
request.session["federate_username"] = username
|
|
||||||
request.session["federate_ticket"] = ticket
|
|
||||||
auth.register_slo(username, request.session.session_key, ticket)
|
|
||||||
url = utils.reverse_params("cas_server:login", params)
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
else:
|
|
||||||
return HttpResponseRedirect(auth.get_login_url())
|
return HttpResponseRedirect(auth.get_login_url())
|
||||||
|
else:
|
||||||
|
ticket = request.GET['ticket']
|
||||||
|
if auth.verify_ticket(ticket):
|
||||||
|
params = utils.copy_params(request.GET, ignore={"ticket"})
|
||||||
|
request.session["federate_username"] = auth.federated_username
|
||||||
|
request.session["federate_ticket"] = ticket
|
||||||
|
auth.register_slo(auth.federated_username, request.session.session_key, ticket)
|
||||||
|
url = utils.reverse_params("cas_server:login", params)
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
else:
|
||||||
|
return HttpResponseRedirect(auth.get_login_url())
|
||||||
|
except FederatedIendityProvider.DoesNotExist:
|
||||||
|
return redirect("cas_server:login")
|
||||||
|
|
||||||
|
|
||||||
class LoginView(View, LogoutMixin):
|
class LoginView(View, LogoutMixin):
|
||||||
|
@ -347,18 +349,11 @@ class LoginView(View, LogoutMixin):
|
||||||
_(u"Invalid login ticket")
|
_(u"Invalid login ticket")
|
||||||
)
|
)
|
||||||
elif ret == self.USER_LOGIN_OK:
|
elif ret == self.USER_LOGIN_OK:
|
||||||
try:
|
self.user = models.User.objects.get_or_create(
|
||||||
self.user = models.User.objects.get(
|
username=self.request.session['username'],
|
||||||
username=self.request.session['username'],
|
session_key=self.request.session.session_key
|
||||||
session_key=self.request.session.session_key
|
)[0]
|
||||||
)
|
self.user.save()
|
||||||
self.user.save() # pragma: no cover (should not happend)
|
|
||||||
except models.User.DoesNotExist:
|
|
||||||
self.user = models.User.objects.create(
|
|
||||||
username=self.request.session['username'],
|
|
||||||
session_key=self.request.session.session_key
|
|
||||||
)
|
|
||||||
self.user.save()
|
|
||||||
elif ret == self.USER_LOGIN_FAILURE: # bad user login
|
elif ret == self.USER_LOGIN_FAILURE: # bad user login
|
||||||
if settings.CAS_FEDERATE:
|
if settings.CAS_FEDERATE:
|
||||||
self.ticket = None
|
self.ticket = None
|
||||||
|
@ -639,8 +634,9 @@ class LoginView(View, LogoutMixin):
|
||||||
else:
|
else:
|
||||||
if (
|
if (
|
||||||
self.request.COOKIES.get('_remember_provider') and
|
self.request.COOKIES.get('_remember_provider') and
|
||||||
self.request.COOKIES['_remember_provider'] in
|
FederatedIendityProvider.objects.filter(
|
||||||
settings.CAS_FEDERATE_PROVIDERS
|
suffix=self.request.COOKIES['_remember_provider']
|
||||||
|
)
|
||||||
):
|
):
|
||||||
params = utils.copy_params(self.request.GET)
|
params = utils.copy_params(self.request.GET)
|
||||||
url = utils.reverse_params(
|
url = utils.reverse_params(
|
||||||
|
@ -708,16 +704,10 @@ class Auth(View):
|
||||||
)
|
)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
try:
|
try:
|
||||||
try:
|
user = models.User.objects.get_or_create(
|
||||||
user = models.User.objects.get(
|
username=form.cleaned_data['username'],
|
||||||
username=form.cleaned_data['username'],
|
session_key=request.session.session_key
|
||||||
session_key=request.session.session_key
|
)[0]
|
||||||
)
|
|
||||||
except models.User.DoesNotExist:
|
|
||||||
user = models.User.objects.create(
|
|
||||||
username=form.cleaned_data['username'],
|
|
||||||
session_key=request.session.session_key
|
|
||||||
)
|
|
||||||
user.save()
|
user.save()
|
||||||
# is the service allowed
|
# is the service allowed
|
||||||
service_pattern = ServicePattern.validate(service)
|
service_pattern = ServicePattern.validate(service)
|
||||||
|
@ -789,6 +779,7 @@ class Validate(View):
|
||||||
return HttpResponse(u"no\n", content_type="text/plain; charset=utf-8")
|
return HttpResponse(u"no\n", content_type="text/plain; charset=utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class ValidateError(Exception):
|
class ValidateError(Exception):
|
||||||
"""handle service validation error"""
|
"""handle service validation error"""
|
||||||
def __init__(self, code, msg=""):
|
def __init__(self, code, msg=""):
|
||||||
|
@ -796,7 +787,7 @@ class ValidateError(Exception):
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
super(ValidateError, self).__init__(code)
|
super(ValidateError, self).__init__(code)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return u"%s" % self.msg
|
return u"%s" % self.msg
|
||||||
|
|
||||||
def render(self, request):
|
def render(self, request):
|
||||||
|
@ -1039,6 +1030,7 @@ class Proxy(View):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
class SamlValidateError(Exception):
|
class SamlValidateError(Exception):
|
||||||
"""handle saml validation error"""
|
"""handle saml validation error"""
|
||||||
def __init__(self, code, msg=""):
|
def __init__(self, code, msg=""):
|
||||||
|
@ -1046,7 +1038,7 @@ class SamlValidateError(Exception):
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
super(SamlValidateError, self).__init__(code)
|
super(SamlValidateError, self).__init__(code)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return u"%s" % self.msg
|
return u"%s" % self.msg
|
||||||
|
|
||||||
def render(self, request):
|
def render(self, request):
|
||||||
|
|
Loading…
Reference in a new issue