├── cas_server
├── tests
│ ├── __init__.py
│ ├── urls.py
│ ├── auth.py
│ ├── test_templatetags.py
│ ├── settings.py
│ ├── mixin.py
│ └── test_utils.py
├── management
│ ├── __init__.py
│ └── commands
│ │ ├── __init__.py
│ │ ├── cas_clean_federate.py
│ │ ├── cas_clean_sessions.py
│ │ └── cas_clean_tickets.py
├── migrations
│ └── __init__.py
├── templatetags
│ ├── __init__.py
│ └── cas_server.py
├── locale
│ ├── pt_BR
│ │ ├── django.mo
│ │ └── django.po
│ ├── fr
│ │ └── LC_MESSAGES
│ │ │ └── django.mo
│ ├── nl
│ │ └── LC_MESSAGES
│ │ │ ├── django.mo
│ │ │ └── django.po
│ └── zh_Hans
│ │ └── LC_MESSAGES
│ │ ├── django.mo
│ │ └── django.po
├── static
│ └── cas_server
│ │ ├── logo.png
│ │ ├── favicon.ico
│ │ ├── bs4
│ │ ├── fa-lock.svg
│ │ ├── fa-user.svg
│ │ └── styles.css
│ │ ├── functions.js
│ │ ├── cas.js
│ │ ├── bs3
│ │ └── styles.css
│ │ └── logo.svg
├── templates
│ └── cas_server
│ │ ├── serviceValidateError.xml
│ │ ├── bs3
│ │ ├── logout.html
│ │ ├── warn.html
│ │ ├── form.html
│ │ ├── logged.html
│ │ ├── login.html
│ │ └── base.html
│ │ ├── bs4
│ │ ├── logout.html
│ │ ├── warn.html
│ │ ├── logged.html
│ │ ├── form.html
│ │ ├── login.html
│ │ └── base.html
│ │ ├── proxy.xml
│ │ ├── samlValidateError.xml
│ │ ├── serviceValidate.xml
│ │ └── samlValidate.xml
├── __init__.py
├── apps.py
├── urls.py
├── federate.py
├── admin.py
├── forms.py
└── default_settings.py
├── docs
├── README.rst
├── CHANGELOG.rst
├── requirements.txt
├── package
│ ├── modules.rst
│ ├── cas_server.forms.rst
│ ├── cas_server.apps.rst
│ ├── cas_server.admin.rst
│ ├── cas_server.models.rst
│ ├── cas_server.cas.rst
│ ├── cas_server.auth.rst
│ ├── cas_server.urls.rst
│ ├── cas_server.utils.rst
│ ├── cas_server.views.rst
│ ├── cas_server.federate.rst
│ ├── cas_server.default_settings.rst
│ ├── cas_server.templatetags.cas_server.rst
│ ├── cas_server.templatetags.rst
│ └── cas_server.rst
├── index.rst
├── Makefile
├── make.bat
└── conf.py
├── requirements.txt
├── pytest.ini
├── requirements-dev.txt
├── setup.cfg
├── .gitignore
├── .coveragerc
├── MANIFEST.in
├── .readthedocs.yaml
├── .update_coverage
├── .gitlab-ci.yml
├── Makefile
├── setup.py
├── .github
└── workflows
│ └── github-actions.yml
└── tox.ini
/cas_server/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cas_server/management/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cas_server/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cas_server/templatetags/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cas_server/management/commands/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/README.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../README.rst
2 |
--------------------------------------------------------------------------------
/docs/CHANGELOG.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../CHANGELOG.rst
2 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | Sphinx
2 | sphinx_rtd_theme
3 | -r ../requirements.txt
4 |
--------------------------------------------------------------------------------
/docs/package/modules.rst:
--------------------------------------------------------------------------------
1 | cas_server
2 | ==========
3 |
4 | .. toctree::
5 | :maxdepth: 4
6 |
7 | cas_server
8 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Django >= 1.11,<5.3
2 | setuptools>=5.5
3 | requests>=2.4
4 | requests_futures>=0.9.5
5 | lxml>=3.4
6 |
--------------------------------------------------------------------------------
/cas_server/locale/pt_BR/django.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nitmir/django-cas-server/HEAD/cas_server/locale/pt_BR/django.mo
--------------------------------------------------------------------------------
/cas_server/static/cas_server/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nitmir/django-cas-server/HEAD/cas_server/static/cas_server/logo.png
--------------------------------------------------------------------------------
/cas_server/static/cas_server/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nitmir/django-cas-server/HEAD/cas_server/static/cas_server/favicon.ico
--------------------------------------------------------------------------------
/cas_server/locale/fr/LC_MESSAGES/django.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nitmir/django-cas-server/HEAD/cas_server/locale/fr/LC_MESSAGES/django.mo
--------------------------------------------------------------------------------
/cas_server/locale/nl/LC_MESSAGES/django.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nitmir/django-cas-server/HEAD/cas_server/locale/nl/LC_MESSAGES/django.mo
--------------------------------------------------------------------------------
/docs/package/cas_server.forms.rst:
--------------------------------------------------------------------------------
1 | cas_server.forms module
2 | =======================
3 |
4 | .. automodule:: cas_server.forms
5 | :members:
6 |
--------------------------------------------------------------------------------
/cas_server/locale/zh_Hans/LC_MESSAGES/django.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nitmir/django-cas-server/HEAD/cas_server/locale/zh_Hans/LC_MESSAGES/django.mo
--------------------------------------------------------------------------------
/docs/package/cas_server.apps.rst:
--------------------------------------------------------------------------------
1 | cas_server.apps module
2 | ======================
3 |
4 | .. automodule:: cas_server.apps
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | testpaths = cas_server/tests/
3 | DJANGO_SETTINGS_MODULE = cas_server.tests.settings
4 | norecursedirs = .* build dist docs
5 | pythonpath = .
6 |
--------------------------------------------------------------------------------
/docs/package/cas_server.admin.rst:
--------------------------------------------------------------------------------
1 | cas_server.admin module
2 | =======================
3 |
4 | .. automodule:: cas_server.admin
5 | :members:
6 | :undoc-members:
7 |
8 |
--------------------------------------------------------------------------------
/docs/package/cas_server.models.rst:
--------------------------------------------------------------------------------
1 | cas_server.models module
2 | ========================
3 |
4 | .. automodule:: cas_server.models
5 | :members:
6 | :undoc-members:
7 |
--------------------------------------------------------------------------------
/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | setuptools>=5.5
2 | requests>=2.4
3 | requests_futures>=0.9.5
4 | lxml>=3.4
5 | tox>=1.8.1
6 | pytest>=7
7 | pytest-django>=2.8.0
8 | pytest-cov>=2.2.1
9 |
--------------------------------------------------------------------------------
/docs/package/cas_server.cas.rst:
--------------------------------------------------------------------------------
1 | cas_server.cas module
2 | =====================
3 |
4 | .. automodule:: cas_server.cas
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/package/cas_server.auth.rst:
--------------------------------------------------------------------------------
1 | cas_server.auth module
2 | ======================
3 |
4 | .. automodule:: cas_server.auth
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/package/cas_server.urls.rst:
--------------------------------------------------------------------------------
1 | cas_server.urls module
2 | ======================
3 |
4 | .. automodule:: cas_server.urls
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/package/cas_server.utils.rst:
--------------------------------------------------------------------------------
1 | cas_server.utils module
2 | =======================
3 |
4 | .. automodule:: cas_server.utils
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/package/cas_server.views.rst:
--------------------------------------------------------------------------------
1 | cas_server.views module
2 | =======================
3 |
4 | .. automodule:: cas_server.views
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/package/cas_server.federate.rst:
--------------------------------------------------------------------------------
1 | cas_server.federate module
2 | ==========================
3 |
4 | .. automodule:: cas_server.federate
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.rst
3 |
4 | [egg_info]
5 | tag_build =
6 | tag_date = 0
7 | tag_svn_revision = 0
8 |
9 | [aliases]
10 | test=pytest
11 |
12 | [bdist_wheel]
13 | universal = 1
14 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/serviceValidateError.xml:
--------------------------------------------------------------------------------
1 |
2 | {{msg}}
3 |
4 |
--------------------------------------------------------------------------------
/docs/package/cas_server.default_settings.rst:
--------------------------------------------------------------------------------
1 | cas_server.default_settings module
2 | ==================================
3 |
4 | .. automodule:: cas_server.default_settings
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/bs3/logout.html:
--------------------------------------------------------------------------------
1 | {% extends "cas_server/bs3/base.html" %}
2 | {% load static %}
3 | {% load i18n %}
4 | {% block content %}
5 |
{{logout_msg}}
6 | {% endblock %}
7 |
8 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/bs4/logout.html:
--------------------------------------------------------------------------------
1 | {% extends "cas_server/bs4/base.html" %}
2 | {% load static %}
3 | {% load i18n %}
4 | {% block content %}
5 | {{logout_msg}}
6 | {% endblock %}
7 |
8 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/proxy.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ticket}}
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/package/cas_server.templatetags.cas_server.rst:
--------------------------------------------------------------------------------
1 | cas_server.templatetags.cas_server module
2 | =========================================
3 |
4 | .. automodule:: cas_server.templatetags.cas_server
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.egg-info
3 | *~
4 | *.swp
5 |
6 | build/
7 | cas/
8 | dist/
9 | db.sqlite3
10 | manage.py
11 | coverage.xml
12 | docs/_build/
13 | docs/django.inv
14 | docs/python.inv
15 |
16 | .tox
17 | test_venv
18 | .coverage
19 | htmlcov/
20 | tox_logs/
21 | .cache/
22 | .eggs/
23 |
--------------------------------------------------------------------------------
/docs/package/cas_server.templatetags.rst:
--------------------------------------------------------------------------------
1 | cas_server.templatetags package
2 | ===============================
3 |
4 | Submodules
5 | ----------
6 |
7 | .. toctree::
8 |
9 | cas_server.templatetags.cas_server
10 |
11 | Module contents
12 | ---------------
13 |
14 | .. automodule:: cas_server.templatetags
15 | :members:
16 | :undoc-members:
17 | :show-inheritance:
18 |
--------------------------------------------------------------------------------
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | branch = True
3 | source = cas_server
4 | omit =
5 | cas_server/migrations*
6 | cas_server/management/*
7 | cas_server/tests/*
8 | cas_server/cas.py
9 |
10 | [report]
11 | exclude_lines =
12 | pragma: no cover
13 | def __repr__
14 | def __unicode__
15 | def __str__
16 | raise AssertionError
17 | raise NotImplementedError
18 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/bs3/warn.html:
--------------------------------------------------------------------------------
1 | {% extends "cas_server/bs3/base.html" %}
2 | {% load static %}
3 | {% load i18n %}
4 |
5 | {% block content %}
6 |
11 | {% endblock %}
12 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/bs4/warn.html:
--------------------------------------------------------------------------------
1 | {% extends "cas_server/bs4/base.html" %}
2 | {% load static %}
3 | {% load i18n %}
4 |
5 | {% block content %}
6 |
11 | {% endblock %}
12 |
--------------------------------------------------------------------------------
/cas_server/static/cas_server/bs4/fa-lock.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/cas_server/static/cas_server/bs4/fa-user.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. django-cas-server documentation master file, created by
2 | sphinx-quickstart on Tue Jul 5 12:11:50 2016.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | django-cas-server documentation
7 | ===============================
8 |
9 | Contents:
10 |
11 | .. toctree::
12 | :maxdepth: 3
13 |
14 | README
15 | package/cas_server
16 |
17 | .. toctree::
18 | :maxdepth: 2
19 |
20 | CHANGELOG
21 |
22 | Indices and tables
23 | ==================
24 |
25 | * :ref:`genindex`
26 |
27 | .. * :ref:`modindex`
28 | .. * :ref:`search`
29 |
30 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include tox.ini
2 | include LICENSE
3 | include README.rst
4 | include CHANGELOG.rst
5 | include .coveragerc
6 | include Makefile
7 | include pytest.ini
8 | include requirements-dev.txt
9 | include requirements.txt
10 | prune .tox
11 | recursive-include cas_server/static *
12 | recursive-include cas_server/templates *
13 | recursive-include cas_server/locale *
14 |
15 | include docs/conf.py
16 | include docs/index.rst
17 | include docs/Makefile
18 | include docs/README.rst
19 | include docs/CHANGELOG.rst
20 | recursive-include docs/_ext *
21 | recursive-include docs/package *
22 | recursive-include docs/_static *
23 | recursive-include docs/_templates *
24 |
--------------------------------------------------------------------------------
/docs/package/cas_server.rst:
--------------------------------------------------------------------------------
1 | cas_server package
2 | ==================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | cas_server.templatetags
10 |
11 | Submodules
12 | ----------
13 |
14 | .. toctree::
15 |
16 | cas_server.admin
17 | cas_server.apps
18 | cas_server.auth
19 | cas_server.cas
20 | cas_server.default_settings
21 | cas_server.federate
22 | cas_server.forms
23 | cas_server.models
24 | cas_server.urls
25 | cas_server.utils
26 | cas_server.views
27 |
28 | Module contents
29 | ---------------
30 |
31 | .. automodule:: cas_server
32 | :members:
33 | :undoc-members:
34 | :show-inheritance:
35 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/samlValidateError.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 | {{msg}}
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/cas_server/static/cas_server/bs4/styles.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | height: 100%;
3 | background-color: #eee;
4 | }
5 | .cover-container {
6 | max-width: 50em;
7 | }
8 |
9 | /* Page title */
10 | #app-name {
11 | color: #000;
12 | text-shadow: 0 .05rem .2rem rgba(255, 255, 255, .5);
13 | }
14 | #app-name img {
15 | width: 110px;
16 | margin-right: 1rem;
17 | }
18 | @media screen and (max-width: 680px) {
19 | #app-name img {
20 | display: block;
21 | margin: auto;
22 | }
23 | }
24 |
25 | /* Add icons to login form */
26 | /* Font-Awesome attribution is already done inside SVG files */
27 | .form-signin input[type="text"] {
28 | background: right 1rem top 50% / 5% no-repeat url('fa-user.svg');
29 | padding-right: 3rem;
30 | }
31 | .form-signin input[type="password"] {
32 | background: right 1rem top 50% / 5% no-repeat url('fa-lock.svg');
33 | padding-right: 3rem;
34 | }
35 |
--------------------------------------------------------------------------------
/cas_server/__init__.py:
--------------------------------------------------------------------------------
1 | # This program is distributed in the hope that it will be useful, but WITHOUT
2 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
3 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
4 | # more details.
5 | #
6 | # You should have received a copy of the GNU General Public License version 3
7 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
8 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
9 | #
10 | # (c) 2015-2025 Valentin Samir
11 | """A django CAS server application"""
12 | try:
13 | import django
14 | except ModuleNotFoundError:
15 | django = None
16 |
17 | #: version of the application
18 | VERSION = '3.1.0'
19 |
20 | if django is None or django.VERSION < (3, 2):
21 | #: path the the application configuration class
22 | default_app_config = 'cas_server.apps.CasAppConfig'
23 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/bs3/form.html:
--------------------------------------------------------------------------------
1 | {% load cas_server %}
2 | {% for error in form.non_field_errors %}
3 |
4 | ×
5 | {{error}}
6 |
7 | {% endfor %}
8 | {% for field in form %}{% if not field|is_hidden %}
9 |
26 | {% else %}{{field}}{% endif %}{% endfor %}
27 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/bs3/logged.html:
--------------------------------------------------------------------------------
1 | {% extends "cas_server/bs3/base.html" %}
2 | {% load i18n %}
3 | {% block content %}
4 | {% blocktrans %}
Log In Successful You have successfully logged into the Central Authentication Service. For security reasons, please Log Out and Exit your web browser when you are done accessing services that require authentication!{% endblocktrans %}
5 |
20 | {% endblock %}
21 |
22 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/bs4/logged.html:
--------------------------------------------------------------------------------
1 | {% extends "cas_server/bs4/base.html" %}
2 | {% load i18n %}
3 | {% block content %}
4 | {% blocktrans %}
Log In Successful You have successfully logged into the Central Authentication Service. For security reasons, please Log Out and Exit your web browser when you are done accessing services that require authentication!{% endblocktrans %}
5 |
20 | {% endblock %}
21 |
22 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # Read the Docs configuration file for Sphinx projects
2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3 |
4 | # Required
5 | version: 2
6 |
7 | # Set the OS, Python version and other tools you might need
8 | build:
9 | os: ubuntu-22.04
10 | tools:
11 | python: "3.12"
12 | # You can also specify other tool versions:
13 | # nodejs: "20"
14 | # rust: "1.70"
15 | # golang: "1.20"
16 |
17 | # Build documentation in the "docs/" directory with Sphinx
18 | sphinx:
19 | configuration: docs/conf.py
20 | # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
21 | # builder: "dirhtml"
22 | # Fail on all warnings to avoid broken references
23 | # fail_on_warning: true
24 |
25 | formats:
26 | - pdf
27 | - epub
28 |
29 | # Optional but recommended, declare the Python requirements required
30 | # to build your documentation
31 | # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
32 | python:
33 | install:
34 | - requirements: docs/requirements.txt
35 |
--------------------------------------------------------------------------------
/cas_server/tests/urls.py:
--------------------------------------------------------------------------------
1 | """cas URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/1.9/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.conf.urls import url, include, include
14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
15 | """
16 |
17 | try:
18 | from django.urls import re_path
19 | except ImportError:
20 | # re_path is not available in Django 2
21 | from django.conf.urls import url as re_path
22 |
23 | from django.conf.urls import include
24 | from django.contrib import admin
25 |
26 | urlpatterns = [
27 | re_path(r'^admin/', admin.site.urls),
28 | re_path(r'^', include('cas_server.urls', namespace='cas_server')),
29 | ]
30 |
--------------------------------------------------------------------------------
/cas_server/management/commands/cas_clean_federate.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This program is distributed in the hope that it will be useful, but WITHOUT
3 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
5 | # more details.
6 | #
7 | # You should have received a copy of the GNU General Public License version 3
8 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
9 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
10 | #
11 | # (c) 2016-2020 Valentin Samir
12 | from django.core.management.base import BaseCommand
13 |
14 | from ... import models
15 |
16 | import sys
17 | if sys.version_info < (3, ):
18 | from django.utils.translation import ugettext_lazy as _
19 | else:
20 | from django.utils.translation import gettext_lazy as _
21 |
22 |
23 | class Command(BaseCommand):
24 | args = ''
25 | help = _(u"Clean old federated users")
26 |
27 | def handle(self, *args, **options):
28 | models.FederatedUser.clean_old_entries()
29 | models.FederateSLO.clean_deleted_sessions()
30 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/bs3/login.html:
--------------------------------------------------------------------------------
1 | {% extends "cas_server/bs3/base.html" %}
2 | {% load i18n %}
3 |
4 | {% block ante_messages %}
5 | {% if auto_submit %}{% endif %}
6 |
7 | {% if auto_submit %} {% endif %}
8 | {% endblock %}
9 | {% block content %}
10 |
17 | {% endblock %}
18 | {% block javascript_inline %}
19 | jQuery(function( $ ){
20 | $("#id_warn").click(function(e){
21 | if($("#id_warn").is(':checked')){
22 | createCookie("warn", "on", 10 * 365);
23 | } else {
24 | eraseCookie("warn");
25 | }
26 | });
27 | });{% if auto_submit %}
28 | document.getElementById('login_form').submit(); // SUBMIT FORM{% endif %}
29 | {% endblock %}
30 |
31 |
--------------------------------------------------------------------------------
/cas_server/apps.py:
--------------------------------------------------------------------------------
1 | # This program is distributed in the hope that it will be useful, but WITHOUT
2 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
3 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
4 | # more details.
5 | #
6 | # You should have received a copy of the GNU General Public License version 3
7 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
8 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
9 | #
10 | # (c) 2015-2020 Valentin Samir
11 | """django config module"""
12 | from django.apps import AppConfig
13 |
14 | import sys
15 | if sys.version_info < (3, ):
16 | from django.utils.translation import ugettext_lazy as _
17 | else:
18 | from django.utils.translation import gettext_lazy as _
19 |
20 |
21 | class CasAppConfig(AppConfig):
22 | """
23 | Bases: :class:`django.apps.AppConfig`
24 |
25 | django CAS application config class
26 | """
27 | #: Full Python path to the application. It must be unique across a Django project.
28 | name = 'cas_server'
29 | #: Human-readable name for the application.
30 | verbose_name = _('Central Authentication Service')
31 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/bs4/form.html:
--------------------------------------------------------------------------------
1 | {% load cas_server %}
2 |
3 | {% for error in form.non_field_errors %}
4 |
5 | ×
6 | {{error}}
7 |
8 | {% endfor %}
9 |
10 | {% for field in form %}{% if not field|is_hidden %}
11 |
33 | {% else %}{{field}}{% endif %}{% endfor %}
34 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/bs4/login.html:
--------------------------------------------------------------------------------
1 | {% extends "cas_server/bs4/base.html" %}
2 | {% load i18n %}
3 |
4 | {% block ante_messages %}
5 | {% if auto_submit %}{% endif %}
6 |
9 | {% if auto_submit %} {% endif %}
10 | {% endblock %}
11 |
12 | {% block content %}
13 |
20 | {% endblock %}
21 |
22 | {% block javascript_inline %}
23 | jQuery(function( $ ){
24 | $("#id_warn").click(function(e){
25 | if($("#id_warn").is(':checked')){
26 | createCookie("warn", "on", 10 * 365);
27 | } else {
28 | eraseCookie("warn");
29 | }
30 | });
31 | });
32 | {% if auto_submit %}document.getElementById('login_form').submit(); // SUBMIT FORM{% endif %}
33 | {% endblock %}
34 |
--------------------------------------------------------------------------------
/cas_server/tests/auth.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This program is distributed in the hope that it will be useful, but WITHOUT
3 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
5 | # more details.
6 | #
7 | # You should have received a copy of the GNU General Public License version 3
8 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
9 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
10 | #
11 | # (c) 2016 Valentin Samir
12 | """Some test authentication classes for the CAS"""
13 | from cas_server import auth
14 |
15 |
16 | class TestCachedAttributesAuthUser(auth.TestAuthUser):
17 | """
18 | A test authentication class only working for one unique user.
19 |
20 | :param unicode username: A username, stored in the :attr:`username`
21 | class attribute. The uniq valid value is ``settings.CAS_TEST_USER``.
22 | """
23 | def attributs(self):
24 | """
25 | The user attributes.
26 |
27 | :raises NotImplementedError: as this class do not support fetching user attributes
28 | """
29 | raise NotImplementedError()
30 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/serviceValidate.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{username}}
4 |
5 | {{auth_date}}
6 | false {# we do not support long-term (Remember-Me) auth #}
7 | {{is_new_login}}
8 | {% for key, value in attributes %} {{value}}
9 | {% endfor %}
10 |
11 |
12 |
13 | {% for key, value in attributes %}
14 | {% endfor %}{% if proxyGrantingTicket %} {{proxyGrantingTicket}}
15 | {% endif %}{% if proxies %}
16 | {% for proxy in proxies %} {{proxy}}
17 | {% endfor %}
18 | {% endif %}
19 |
20 |
--------------------------------------------------------------------------------
/cas_server/management/commands/cas_clean_sessions.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This program is distributed in the hope that it will be useful, but WITHOUT
3 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
5 | # more details.
6 | #
7 | # You should have received a copy of the GNU General Public License version 3
8 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
9 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
10 | #
11 | # (c) 2016-2020 Valentin Samir
12 | """Clean deleted sessions management command"""
13 | from django.core.management.base import BaseCommand
14 |
15 | from ... import models
16 |
17 | import sys
18 | if sys.version_info < (3,):
19 | from django.utils.translation import ugettext_lazy as _
20 | else:
21 | from django.utils.translation import gettext_lazy as _
22 |
23 |
24 | class Command(BaseCommand):
25 | """Clean deleted sessions"""
26 | args = ''
27 | help = _(u"Clean deleted sessions")
28 |
29 | def handle(self, *args, **options):
30 | models.User.clean_deleted_sessions()
31 | models.UserAttributes.clean_old_entries()
32 | models.NewVersionWarning.send_mails()
33 |
--------------------------------------------------------------------------------
/cas_server/management/commands/cas_clean_tickets.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This program is distributed in the hope that it will be useful, but WITHOUT
3 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
5 | # more details.
6 | #
7 | # You should have received a copy of the GNU General Public License version 3
8 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
9 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
10 | #
11 | # (c) 2016-2020 Valentin Samir
12 | """Clean old trickets management command"""
13 | from django.core.management.base import BaseCommand
14 |
15 | from ... import models
16 |
17 | import sys
18 | if sys.version_info < (3, ):
19 | from django.utils.translation import ugettext_lazy as _
20 | else:
21 | from django.utils.translation import gettext_lazy as _
22 |
23 |
24 | class Command(BaseCommand):
25 | """Clean old trickets"""
26 | args = ''
27 | help = _(u"Clean old tickets")
28 |
29 | def handle(self, *args, **options):
30 | models.User.clean_old_entries()
31 | for ticket_class in [models.ServiceTicket, models.ProxyTicket, models.ProxyGrantingTicket]:
32 | ticket_class.clean_old_entries()
33 |
--------------------------------------------------------------------------------
/cas_server/static/cas_server/functions.js:
--------------------------------------------------------------------------------
1 | function createCookie(name, value, days){
2 | var expires;
3 | var date;
4 | if(days){
5 | date = new Date();
6 | date.setTime(date.getTime()+(days*24*60*60*1000));
7 | expires = "; expires="+date.toGMTString();
8 | }
9 | else{
10 | expires = "";
11 | }
12 | document.cookie = name + "=" + value + expires + "; path=/";
13 | }
14 |
15 | function readCookie(name){
16 | var nameEQ = name + "=";
17 | var ca = document.cookie.split(";");
18 | for(var i=0;i < ca.length;i++) {
19 | var c = ca[i];
20 | while (c.charAt(0) === " "){
21 | c = c.substring(1,c.length);
22 | }
23 | if (c.indexOf(nameEQ) === 0){
24 | return c.substring(nameEQ.length,c.length);
25 | }
26 | }
27 | return null;
28 | }
29 |
30 | function eraseCookie(name) {
31 | createCookie(name,"",-1);
32 | }
33 |
34 | function discard_and_remember(id, cookie_name, token, days=10*365){
35 | jQuery(function( $ ){
36 | $(id).click(function( e ){
37 | e.preventDefault();
38 | createCookie(cookie_name, token, days);
39 | });
40 | if(readCookie(cookie_name) === token){
41 | $(id).parent().hide();
42 | }
43 | });
44 | }
45 |
--------------------------------------------------------------------------------
/cas_server/tests/test_templatetags.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This program is distributed in the hope that it will be useful, but WITHOUT
3 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
5 | # more details.
6 | #
7 | # You should have received a copy of the GNU General Public License version 3
8 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
9 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
10 | #
11 | # (c) 2016 Valentin Samir
12 | """tests for the customs template tags"""
13 | from django.test import TestCase
14 |
15 | from cas_server import forms
16 | from cas_server.templatetags import cas_server
17 |
18 |
19 | class TemplateTagsTestCase(TestCase):
20 | """tests for the customs template tags"""
21 |
22 | def test_is_checkbox(self):
23 | """test for the template filter is_checkbox"""
24 | form = forms.UserCredential()
25 | self.assertFalse(cas_server.is_checkbox(form["username"]))
26 | self.assertTrue(cas_server.is_checkbox(form["warn"]))
27 |
28 | def test_is_hidden(self):
29 | """test for the template filter is_hidden"""
30 | form = forms.UserCredential()
31 | self.assertFalse(cas_server.is_hidden(form["username"]))
32 | self.assertTrue(cas_server.is_hidden(form["lt"]))
33 |
--------------------------------------------------------------------------------
/cas_server/templatetags/cas_server.py:
--------------------------------------------------------------------------------
1 | # This program is distributed in the hope that it will be useful, but WITHOUT
2 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
3 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
4 | # more details.
5 | #
6 | # You should have received a copy of the GNU General Public License version 3
7 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
8 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
9 | #
10 | # (c) 2015-2016 Valentin Samir
11 | """template tags for the app"""
12 | from django import template
13 | from django import forms
14 |
15 | register = template.Library()
16 |
17 |
18 | @register.filter(name='is_checkbox')
19 | def is_checkbox(field):
20 | """
21 | check if a form bound field is a checkbox
22 |
23 | :param django.forms.BoundField field: A bound field
24 | :return: ``True`` if the field is a checkbox, ``False`` otherwise.
25 | :rtype: bool
26 | """
27 | return isinstance(field.field.widget, forms.CheckboxInput)
28 |
29 |
30 | @register.filter(name='is_hidden')
31 | def is_hidden(field):
32 | """
33 | check if a form bound field is hidden
34 |
35 | :param django.forms.BoundField field: A bound field
36 | :return: ``True`` if the field is hidden, ``False`` otherwise.
37 | :rtype: bool
38 | """
39 | return isinstance(field.field.widget, forms.HiddenInput)
40 |
--------------------------------------------------------------------------------
/.update_coverage:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -x
3 | set -e
4 | BASEDIR="$(realpath "$1")"
5 | PROJECT_NAME="$2"
6 |
7 | TITLE="Coverage report of $PROJECT_NAME"
8 |
9 | # build by gitlab CI
10 | if [ -n "$CI_BUILD_REF_NAME" ]; then
11 | BRANCH="$CI_BUILD_REF_NAME"
12 | TITLE="$TITLE, $BRANCH branch"
13 | # build by travis
14 | elif [ -n "$TRAVIS_BRANCH" ]; then
15 | # if this a pull request ?
16 | if [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then
17 | BRANCH="pull-request-$TRAVIS_PULL_REQUEST"
18 | TITLE="$TITLE, pull request n°$BRANCH"
19 | else
20 | BRANCH="$TRAVIS_BRANCH"
21 | TITLE="$TITLE, $BRANCH branch"
22 | fi
23 | else
24 | BRANCH="$(git rev-parse --abbrev-ref HEAD)"
25 | TITLE="$TITLE, $BRANCH branch"
26 | fi
27 |
28 | if [[ "$BRANCH" = "HEAD" ]] || [ -z "$BRANCH" ]; then
29 | echo "bad branch name '$BRANCH', ignoring it"
30 | exit 0
31 | fi
32 |
33 |
34 | VENV="$(mktemp -d)"
35 | HTMLREPORT="$(mktemp -d)"
36 | virtualenv -p python3 "$VENV"
37 | "$VENV/bin/pip" install coverage
38 | "$VENV/bin/coverage" html --title "$TITLE" --directory "$HTMLREPORT"
39 | rm -rf "$VENV"
40 |
41 |
42 | cd "$HTMLREPORT"; tar czf "$BASEDIR/coverage.tar.gz" ./
43 |
44 | cd "$BASEDIR"
45 |
46 | rm -rf "$HTMLREPORT"
47 |
48 | set +x
49 | echo "curl https://badges.genua.fr/coverage/ ..."
50 | curl https://badges.genua.fr/coverage/ \
51 | -F "secret=$COVERAGE_TOKEN" \
52 | -F "tar=@$BASEDIR/coverage.tar.gz" \
53 | -F "project=$PROJECT_NAME" \
54 | -F "branch=$BRANCH"
55 |
56 | set -x
57 | rm "$BASEDIR/coverage.tar.gz"
58 |
59 |
--------------------------------------------------------------------------------
/cas_server/static/cas_server/cas.js:
--------------------------------------------------------------------------------
1 | function cas_login(cas_server_login, service, login_service, callback){
2 | var url = cas_server_login + "?service=" + encodeURIComponent(service);
3 | $.ajax({
4 | type: "GET",
5 | url,
6 | beforeSend(request) {
7 | request.setRequestHeader("X-AJAX", "1");
8 | },
9 | xhrFields: {
10 | withCredentials: true
11 | },
12 | success(data, textStatus, request){
13 | if(data.status === "success"){
14 | $.ajax({
15 | type: "GET",
16 | url: data.url,
17 | xhrFields: {
18 | withCredentials: true
19 | },
20 | success: callback,
21 | error(request, textStatus, errorThrown) {},
22 | });
23 | } else {
24 | if(data.detail === "login required"){
25 | window.location.href = cas_server_login + "?service=" + encodeURIComponent(login_service);
26 | } else {
27 | alert("error: " + data.messages[1].message);
28 | }
29 | }
30 | },
31 | error(request, textStatus, errorThrown) {},
32 | });
33 | }
34 |
35 | function cas_logout(cas_server_logout){
36 | $.ajax({
37 | type: "GET",
38 | url: cas_server_logout,
39 | beforeSend(request) {
40 | request.setRequestHeader("X-AJAX", "1");
41 | },
42 | xhrFields: {
43 | withCredentials: true
44 | },
45 | error(request, textStatus, errorThrown) {},
46 | success(data, textStatus, request){
47 | if(data.status === "error"){
48 | alert("error: " + data.messages[1].message);
49 | }
50 | },
51 | });
52 | }
53 |
--------------------------------------------------------------------------------
/cas_server/static/cas_server/bs3/styles.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | height: 100%;
3 | }
4 | body {
5 | padding-top: 40px;
6 | padding-bottom: 0;
7 | background-color: #eee;
8 | }
9 |
10 | .form-signin .form-signin-heading,
11 | .form-signin .checkbox {
12 | margin-bottom: 10px;
13 | }
14 | .form-signin .checkbox {
15 | font-weight: normal;
16 | }
17 | .form-signin .form-control {
18 | position: relative;
19 | height: auto;
20 | -webkit-box-sizing: border-box;
21 | -moz-box-sizing: border-box;
22 | box-sizing: border-box;
23 | padding: 10px;
24 | font-size: 16px;
25 | }
26 | .form-signin .form-control:focus {
27 | z-index: 2;
28 | }
29 | .form-signin input[type="text"] {
30 | margin-bottom: -1px;
31 | border-bottom-right-radius: 0;
32 | border-bottom-left-radius: 0;
33 | }
34 | .form-signin input[type="password"] {
35 | margin-bottom: 10px;
36 | border-top-left-radius: 0;
37 | border-top-right-radius: 0;
38 | }
39 |
40 | #app-name {
41 | text-align: center;
42 | }
43 | #app-name img {
44 | width:110px;
45 | }
46 |
47 | /* Wrapper for page content to push down footer */
48 | #wrap {
49 | min-height: 100%;
50 | height: auto;
51 | height: 100%;
52 | /* Negative indent footer by it's height */
53 | margin: 0 auto -40px;
54 | }
55 | #footer {
56 | height: 40px;
57 | text-align: center;
58 | }
59 | #footer p {
60 | padding-top: 10px;
61 | }
62 |
63 | @media screen and (max-width: 680px) {
64 | #app-name {
65 | margin: 0;
66 | }
67 | #app-name img {
68 | display: block;
69 | margin: auto;
70 | }
71 | body {
72 | padding-top: 0;
73 | padding-bottom: 0;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | before_script:
2 | - pip install tox setuptools
3 |
4 | flake8:
5 | image: "python:3.9"
6 | cache:
7 | key: flake8
8 | paths:
9 | - .tox/flake8
10 | script:
11 | - tox -e flake8
12 |
13 | check_rst:
14 | image: "python:3.9"
15 | cache:
16 | key: check_rst
17 | paths:
18 | - .tox/check_rst
19 | script:
20 | - tox -e check_rst
21 |
22 | py27-django111:
23 | image: "python:2.7"
24 | cache:
25 | key: py27-django111
26 | paths:
27 | - .tox/py27-django111
28 | script:
29 | - tox -e py27-django111
30 |
31 | py35-django111:
32 | image: "python:3.5"
33 | cache:
34 | key: py35-django111
35 | paths:
36 | - .tox/py35-django111
37 | script:
38 | - tox -e py35-django111
39 |
40 | py36-django111:
41 | image: "python:3.6"
42 | cache:
43 | key: py36-django111
44 | paths:
45 | - .tox/py36-django111
46 | script:
47 | - tox -e py36-django111
48 |
49 | py37-django22:
50 | image: "python:3.7"
51 | cache:
52 | key: py37-django22
53 | paths:
54 | - .tox/py37-django22
55 | script:
56 | - tox -e py37-django22
57 |
58 | py38-django22:
59 | image: "python:3.8"
60 | cache:
61 | key: py38-django22
62 | paths:
63 | - .tox/py38-django22
64 | script:
65 | - tox -e py38-django22
66 |
67 | py38-django30:
68 | image: "python:3.8"
69 | cache:
70 | key: py38-django30
71 | paths:
72 | - .tox/py38-django30
73 | script:
74 | - tox -e py38-django30
75 |
76 | py38-django31:
77 | image: "python:3.8"
78 | cache:
79 | key: py38-django31
80 | paths:
81 | - .tox/py38-django31
82 | script:
83 | - tox -e py38-django31
84 |
85 | py39-django22:
86 | image: "python:3.9"
87 | cache:
88 | key: py39-django22
89 | paths:
90 | - .tox/py39-django22
91 | script:
92 | - tox -e py39-django22
93 |
94 | py39-django30:
95 | image: "python:3.9"
96 | cache:
97 | key: py39-django30
98 | paths:
99 | - .tox/py39-django30
100 | script:
101 | - tox -e py39-django30
102 |
103 | py39-django31:
104 | image: "python:3.9"
105 | cache:
106 | key: py39-django31
107 | paths:
108 | - .tox/py39-django31
109 | script:
110 | - tox -e py39-django31
111 |
112 | coverage:
113 | image: "python:3.9"
114 | cache:
115 | key: coverage
116 | paths:
117 | - .tox/coverage
118 | script:
119 | - tox -e coverage
120 |
--------------------------------------------------------------------------------
/cas_server/urls.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This program is distributed in the hope that it will be useful, but WITHOUT
3 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
5 | # more details.
6 | #
7 | # You should have received a copy of the GNU General Public License version 3
8 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
9 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
10 | #
11 | # (c) 2015-2021 Valentin Samir
12 | """urls for the app"""
13 |
14 | try:
15 | from django.urls import re_path
16 | except ImportError:
17 | # re_path is not available in Django 2
18 | from django.conf.urls import url as re_path
19 |
20 | from django.views.generic import RedirectView
21 | from django.views.decorators.debug import sensitive_post_parameters, sensitive_variables
22 |
23 | from cas_server import views
24 |
25 | app_name = "cas_server"
26 |
27 | urlpatterns = [
28 | re_path(
29 | r'^$',
30 | RedirectView.as_view(pattern_name="cas_server:login", permanent=False, query_string=True)
31 | ),
32 | re_path(
33 | '^login$',
34 | sensitive_post_parameters('password')(
35 | views.LoginView.as_view()
36 | ),
37 | name='login'
38 | ),
39 | re_path('^logout$', views.LogoutView.as_view(), name='logout'),
40 | re_path('^validate$', views.Validate.as_view(), name='validate'),
41 | re_path(
42 | '^serviceValidate$',
43 | views.ValidateService.as_view(allow_proxy_ticket=False),
44 | name='serviceValidate'
45 | ),
46 | re_path(
47 | '^proxyValidate$',
48 | views.ValidateService.as_view(allow_proxy_ticket=True),
49 | name='proxyValidate'
50 | ),
51 | re_path('^proxy$', views.Proxy.as_view(), name='proxy'),
52 | re_path(
53 | '^p3/serviceValidate$',
54 | views.ValidateService.as_view(allow_proxy_ticket=False),
55 | name='p3_serviceValidate'
56 | ),
57 | re_path(
58 | '^p3/proxyValidate$',
59 | views.ValidateService.as_view(allow_proxy_ticket=True),
60 | name='p3_proxyValidate'
61 | ),
62 | re_path('^samlValidate$', views.SamlValidate.as_view(), name='samlValidate'),
63 | re_path(
64 | '^auth$',
65 | sensitive_variables('password', 'secret')(
66 | sensitive_post_parameters('password', 'secret')(
67 | views.Auth.as_view()
68 | )
69 | ),
70 | name='auth'
71 | ),
72 | re_path("^federate(?:/(?P([^/]+)))?$",
73 | views.FederateAuth.as_view(), name='federateAuth'),
74 | ]
75 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/samlValidate.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 | {{Recipient}}
20 |
21 |
22 |
23 |
24 |
25 | {{username}}
26 |
27 |
28 | urn:oasis:names:tc:SAML:1.0:cm:artifact
29 |
30 |
31 |
32 |
33 | {{auth_date}}
34 |
35 |
36 | false {# we do not support long-term (Remember-Me) auth #}
37 |
38 |
39 | {{is_new_login}}
40 |
41 | {% for name, value in attributes %}
42 | {{value}}
43 |
44 | {% endfor %}
45 |
47 |
48 | {{username}}
49 |
50 |
51 | urn:oasis:names:tc:SAML:1.0:cm:artifact
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: build dist docs
2 | VERSION=`python3 setup.py -V`
3 |
4 | WHL_FILES := $(wildcard dist/*.whl)
5 | WHL_ASC := $(WHL_FILES:=.asc)
6 | DIST_FILE := $(wildcard dist/*.tar.gz)
7 | DIST_ASC := $(DIST_FILE:=.asc)
8 |
9 | build:
10 | python3 setup.py build
11 |
12 | install: dist
13 | pip3 -V
14 | pip3 install --no-cache-dir --no-deps --upgrade --force-reinstall --find-links ./dist/django-cas-server-${VERSION}.tar.gz django-cas-server
15 |
16 | uninstall:
17 | pip3 uninstall django-cas-server || true
18 |
19 | clean_pyc:
20 | find ./ -name '*.pyc' -delete
21 | find ./ -name __pycache__ -delete
22 | clean_build:
23 | rm -rf build django_cas_server.egg-info dist
24 | clean_tox:
25 | rm -rf .tox tox_logs
26 | clean_test_venv:
27 | rm -rf test_venv
28 | clean_coverage:
29 | rm -rf coverage.xml .coverage htmlcov
30 | clean_tild_backup:
31 | find ./ -name '*~' -delete
32 | clean_docs:
33 | rm -rf docs/_build/ docs/django.inv docs/python.inv
34 | clean_eggs:
35 | rm -rf .eggs/
36 |
37 | clean: clean_pyc clean_build clean_coverage clean_tild_backup
38 |
39 | clean_all: clean clean_tox clean_test_venv clean_docs clean_eggs
40 |
41 | dist:
42 | python3 setup.py sdist
43 | python3 setup.py bdist_wheel
44 |
45 | test_venv/bin/python:
46 | python3 -m venv test_venv
47 | test_venv/bin/pip install -U --requirement requirements-dev.txt 'Django>=5.2,<5.3'
48 |
49 | test_venv/cas/manage.py: test_venv
50 | mkdir -p test_venv/cas
51 | test_venv/bin/django-admin startproject cas test_venv/cas
52 | ln -s ../../cas_server test_venv/cas/cas_server
53 | sed -i "s/'django.contrib.staticfiles',/'django.contrib.staticfiles',\n 'cas_server',/" test_venv/cas/cas/settings.py
54 | sed -i "s/'django.middleware.clickjacking.XFrameOptionsMiddleware',/'django.middleware.clickjacking.XFrameOptionsMiddleware',\n 'django.middleware.locale.LocaleMiddleware',/" test_venv/cas/cas/settings.py
55 | sed -i 's/from django.urls import path/from django.urls import path, include/' test_venv/cas/cas/urls.py
56 | sed -i "s@path('admin/', admin.site.urls),@path('admin/', admin.site.urls),\n path('', include('cas_server.urls', namespace='cas_server')),@" test_venv/cas/cas/urls.py
57 | test_venv/bin/python test_venv/cas/manage.py migrate
58 | test_venv/bin/python test_venv/cas/manage.py createsuperuser
59 |
60 | test_venv: test_venv/bin/python
61 |
62 | test_project: test_venv/cas/manage.py
63 | @echo "##############################################################"
64 | @echo "A test django project was created in $(realpath test_venv/cas)"
65 |
66 | run_server: test_project
67 | test_venv/bin/python test_venv/cas/manage.py runserver
68 |
69 | run_tests: test_venv
70 | python3 setup.py check --restructuredtext --stric
71 | test_venv/bin/py.test -rw -x --cov=cas_server --cov-report html --cov-report term
72 | rm htmlcov/coverage_html.js # I am really pissed off by those keybord shortcuts
73 |
74 | test_venv/bin/sphinx-build: test_venv
75 | test_venv/bin/pip install -r docs/requirements.txt
76 |
77 | docs: test_venv/bin/sphinx-build
78 | bash -c "source test_venv/bin/activate; cd docs; make html"
79 |
80 | sign_release: $(WHL_ASC) $(DIST_ASC)
81 |
82 | dist/%.asc:
83 | gpg --detach-sign -a $(@:.asc=)
84 |
85 | test_venv/bin/twine: test_venv
86 | test_venv/bin/pip install twine
87 |
88 | publish_pypi_release: test_venv test_venv/bin/twine dist sign_release
89 | test_venv/bin/twine upload --sign dist/*
90 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pkg_resources
3 | from setuptools import setup
4 | from cas_server import VERSION
5 |
6 | with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme:
7 | README = readme.read()
8 |
9 | if __name__ == '__main__':
10 | # allow setup.py to be run from any path
11 | os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
12 |
13 | setup(
14 | name='django-cas-server',
15 | version=VERSION,
16 | packages=[
17 | 'cas_server', 'cas_server.migrations',
18 | 'cas_server.management', 'cas_server.management.commands',
19 | 'cas_server.tests', 'cas_server.templatetags'
20 | ],
21 | include_package_data=True,
22 | license='GPLv3',
23 | description=(
24 | 'A Django Central Authentication Service server '
25 | 'implementing the CAS Protocol 3.0 Specification'
26 | ),
27 | long_description=README,
28 | author='Valentin Samir',
29 | author_email='valentin.samir@crans.org',
30 | classifiers=[
31 | 'Environment :: Web Environment',
32 | 'Development Status :: 5 - Production/Stable',
33 | 'Framework :: Django',
34 | 'Framework :: Django :: 1.11',
35 | 'Framework :: Django :: 2.2',
36 | 'Framework :: Django :: 3.2',
37 | 'Framework :: Django :: 4.2',
38 | 'Framework :: Django :: 5.2',
39 | 'Intended Audience :: Developers',
40 | 'Intended Audience :: System Administrators',
41 | 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
42 | 'Operating System :: OS Independent',
43 | 'Programming Language :: Python',
44 | 'Programming Language :: Python :: 3',
45 | 'Programming Language :: Python :: 3.6',
46 | 'Programming Language :: Python :: 3.7',
47 | 'Programming Language :: Python :: 3.8',
48 | 'Programming Language :: Python :: 3.9',
49 | 'Programming Language :: Python :: 3.10',
50 | 'Programming Language :: Python :: 3.11',
51 | 'Programming Language :: Python :: 3.12',
52 | 'Programming Language :: Python :: 3.13',
53 | 'Topic :: Software Development :: Libraries :: Python Modules',
54 | 'Topic :: Internet :: WWW/HTTP',
55 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
56 | 'Topic :: System :: Systems Administration :: Authentication/Directory'
57 | ],
58 | package_data={
59 | 'cas_server': [
60 | 'templates/cas_server/*',
61 | 'static/cas_server/*',
62 | 'locale/*/LC_MESSAGES/*',
63 | ]
64 | },
65 | keywords=['django', 'cas', 'cas3', 'server', 'sso', 'single sign-on', 'authentication', 'auth'],
66 | install_requires=[
67 | 'Django >= 1.11,<5.3', 'requests >= 2.4', 'requests_futures >= 0.9.5',
68 | 'lxml >= 3.4'
69 | ],
70 | url="https://github.com/nitmir/django-cas-server",
71 | download_url="https://github.com/nitmir/django-cas-server/releases/latest",
72 | zip_safe=False,
73 | setup_requires=[],
74 | tests_require=['pytest', 'pytest-django', 'pytest-pythonpath', 'pytest-warnings', 'mock>=1'],
75 | )
76 |
--------------------------------------------------------------------------------
/cas_server/tests/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django test settings for cas_server application.
3 |
4 | Generated by 'django-admin startproject' using Django 1.9.7.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.9/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/1.9/ref/settings/
11 | """
12 |
13 | import os
14 | import django
15 |
16 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
17 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
18 |
19 |
20 | # Quick-start development settings - unsuitable for production
21 | # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
22 |
23 | # SECURITY WARNING: keep the secret key used in production secret!
24 | SECRET_KEY = 'changeme'
25 |
26 | # SECURITY WARNING: don't run with debug turned on in production!
27 | DEBUG = True
28 |
29 | ALLOWED_HOSTS = []
30 |
31 |
32 | # Application definition
33 |
34 | INSTALLED_APPS = [
35 | 'django.contrib.admin',
36 | 'django.contrib.auth',
37 | 'django.contrib.contenttypes',
38 | 'django.contrib.sessions',
39 | 'django.contrib.messages',
40 | 'django.contrib.staticfiles',
41 | 'cas_server',
42 | ]
43 |
44 | MIDDLEWARE = [
45 | 'django.contrib.sessions.middleware.SessionMiddleware',
46 | 'django.middleware.common.CommonMiddleware',
47 | 'django.middleware.csrf.CsrfViewMiddleware',
48 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
49 | 'django.contrib.messages.middleware.MessageMiddleware',
50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
51 | 'django.middleware.locale.LocaleMiddleware',
52 | ]
53 | if django.VERSION < (1, 10):
54 | MIDDLEWARE_CLASSES = MIDDLEWARE
55 |
56 | TEMPLATES = [
57 | {
58 | 'APP_DIRS': True,
59 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
60 | 'DIRS': [],
61 | 'OPTIONS': {
62 | 'context_processors': [
63 | 'django.template.context_processors.debug',
64 | 'django.template.context_processors.request',
65 | 'django.contrib.auth.context_processors.auth',
66 | 'django.contrib.messages.context_processors.messages'
67 | ]
68 | }
69 | }
70 | ]
71 |
72 | ROOT_URLCONF = 'cas_server.tests.urls'
73 |
74 | # Database
75 | # https://docs.djangoproject.com/en/1.9/ref/settings/#databases
76 |
77 | DATABASES = {
78 | 'default': {
79 | 'ENGINE': 'django.db.backends.sqlite3',
80 | 'NAME': ':memory:',
81 | }
82 | }
83 |
84 | # Internationalization
85 | # https://docs.djangoproject.com/en/1.9/topics/i18n/
86 |
87 | LANGUAGE_CODE = 'en-us'
88 |
89 | TIME_ZONE = 'UTC'
90 |
91 | USE_I18N = True
92 |
93 | if django.VERSION < (4, 0):
94 | USE_L10N = True
95 |
96 | USE_TZ = True
97 |
98 |
99 | # Static files (CSS, JavaScript, Images)
100 | # https://docs.djangoproject.com/en/1.9/howto/static-files/
101 |
102 | STATIC_URL = '/static/'
103 |
104 | CAS_NEW_VERSION_HTML_WARNING = False
105 | CAS_NEW_VERSION_EMAIL_WARNING = False
106 |
107 | LOGGING = {
108 | 'version': 1,
109 | 'disable_existing_loggers': False,
110 | 'formatters': {
111 | 'cas_file': {
112 | 'format': '%(asctime)s %(levelname)s %(message)s'
113 | },
114 | },
115 | 'handlers': {
116 | 'cas_stream': {
117 | 'level': 'INFO',
118 | 'class': 'logging.StreamHandler',
119 | 'formatter': 'cas_file',
120 | },
121 | },
122 | 'loggers': {
123 | 'cas_server': {
124 | 'handlers': ['cas_stream'],
125 | 'level': 'INFO',
126 | 'propagate': True,
127 | },
128 | },
129 | }
130 |
--------------------------------------------------------------------------------
/.github/workflows/github-actions.yml:
--------------------------------------------------------------------------------
1 | name: django-cas-server
2 | run-name: ${{ github.actor }} is running django-cas-server CI tests
3 | on: [push]
4 | concurrency:
5 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
6 | cancel-in-progress: true
7 | jobs:
8 | flake8:
9 | runs-on: ubuntu-latest
10 | container:
11 | image: python:bookworm
12 | steps:
13 | - uses: actions/checkout@v3
14 | - run: pip install tox
15 | - run: tox -e flake8
16 | check_rst:
17 | runs-on: ubuntu-latest
18 | container:
19 | image: python:bookworm
20 | steps:
21 | - uses: actions/checkout@v3
22 | - run: pip install tox
23 | - run: tox -e check_rst
24 | coverage:
25 | runs-on: ubuntu-latest
26 | container:
27 | image: python:bookworm
28 | steps:
29 | - uses: actions/checkout@v3
30 | - run: pip install tox
31 | - run: tox -e coverage
32 | env:
33 | COVERAGE_TOKEN: ${{ secrets.COVERAGE_TOKEN }}
34 | tests:
35 | runs-on: ubuntu-latest
36 | strategy:
37 | fail-fast: false
38 | matrix:
39 | arch: ["amd64"]
40 | tox:
41 | # REHL 7 support and Ubuntu bionic
42 | - python: "3.6"
43 | env: "py36-django111"
44 | # RHEL 8 support
45 | - python: "3.6"
46 | env: "py36-django22"
47 | # Debian buster support
48 | - python: "3.7"
49 | env: "py37-django111"
50 | # Ubuntu focal support
51 | - python: "3.8"
52 | env: "py38-django22"
53 | # Debian bullseye
54 | - python: "3.9"
55 | env: "py39-django22"
56 | # Debian bookworm
57 | - python: "3.11"
58 | env: "py311-django32"
59 | # Django additional supported version
60 | - python: "3.10"
61 | env: "py310-django52"
62 | - python: "3.11"
63 | env: "py311-django52"
64 | - python: "3.12"
65 | env: "py312-django52"
66 | - python: "3.13"
67 | env: "py313-django52"
68 | steps:
69 | - uses: actions/checkout@v3
70 | - if: matrix.arch != 'amd64'
71 | name: "Install docker multiarch support"
72 | run: |
73 | sudo docker run --rm --privileged tonistiigi/binfmt --install ${{ matrix.arch }}
74 | sudo docker run --rm --privileged multiarch/qemu-user-static --reset -p yes --credential yes
75 | - name: "Check docker arch ${{ matrix.arch }} support"
76 | run: sudo docker run --platform linux/${{ matrix.arch }} --rm ${{ matrix.arch }}/python:${{ matrix.tox.python }} /bin/bash -c "echo -n \"Running with arch \"; uname -m;"
77 | - name: "Run tests on arch ${{ matrix.arch }}"
78 | run: sudo docker run --platform linux/${{ matrix.arch }} --rm -v $(pwd):$(pwd) ${{ matrix.arch }}/python:${{ matrix.tox.python }} /bin/bash -c "cd $(pwd); uname -m; pip install tox; tox -e ${{ matrix.tox.env }}"
79 | tests-ubuntu:
80 | runs-on: ubuntu-latest
81 | strategy:
82 | fail-fast: false
83 | matrix:
84 | arch: ["amd64", "ppc64le"]
85 | tox:
86 | # Ubuntu jammy
87 | - python: "3.10"
88 | env: "py310-django32"
89 | # Ubuntu noble
90 | - python: "3.12"
91 | env: "py312-django42"
92 | steps:
93 | - uses: actions/checkout@v3
94 | - if: matrix.arch != 'amd64'
95 | name: "Install docker multiarch support"
96 | run: |
97 | sudo docker run --rm --privileged tonistiigi/binfmt --install ${{ matrix.arch }}
98 | sudo docker run --rm --privileged multiarch/qemu-user-static --reset -p yes --credential yes
99 | - name: "Check docker arch ${{ matrix.arch }} support"
100 | run: sudo docker run --platform linux/${{ matrix.arch }} --rm ${{ matrix.arch }}/python:${{ matrix.tox.python }} /bin/bash -c "echo -n \"Running with arch \"; uname -m;"
101 | - name: "Run tests on arch ${{ matrix.arch }}"
102 | run: sudo docker run --platform linux/${{ matrix.arch }} --rm -v $(pwd):$(pwd) ${{ matrix.arch }}/python:${{ matrix.tox.python }} /bin/bash -c "cd $(pwd); uname -m; pip install tox; tox -e ${{ matrix.tox.env }}"
103 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/bs4/base.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}{% load static %}{% get_current_language as LANGUAGE_CODE %}
2 |
3 |
4 |
5 |
6 | {% block title %}{% trans "Central Authentication Service" %}{% endblock %}
7 |
8 | {% if settings.CAS_FAVICON_URL %} {% endif %}
9 |
10 |
11 |
12 |
13 | {% if auto_submit %}
{% endif %}
14 |
32 | {% if auto_submit %} {% endif %}
33 |
34 |
35 | {% block ante_messages %}{% endblock %}
36 |
37 | {% for message in messages %}
38 |
53 | {% endfor %}
54 | {% block content %}{% endblock %}
55 |
56 |
57 |
58 | {% if settings.CAS_SHOW_POWERED %}
59 |
66 | {% endif %}
67 |
68 |
69 |
70 |
71 |
72 |
83 | {% block javascript %}{% endblock %}
84 |
85 |
86 |
92 |
--------------------------------------------------------------------------------
/cas_server/templates/cas_server/bs3/base.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}{% load static %}{% get_current_language as LANGUAGE_CODE %}
2 |
3 |
4 |
5 |
6 |
7 | {% block title %}{% trans "Central Authentication Service" %}{% endblock %}
8 |
9 |
10 |
11 |
15 | {% if settings.CAS_FAVICON_URL %} {% endif %}
16 |
17 |
18 |
19 |
20 |
21 | {% if auto_submit %}
{% endif %}
22 |
23 |
24 |
25 | {% if settings.CAS_LOGO_URL %} {% endif %}
26 | {% trans "Central Authentication Service" %}
27 |
28 |
29 | {% if auto_submit %} {% endif %}
30 |
31 |
32 |
33 | {% if auto_submit %}
{% endif %}
34 | {% for msg in CAS_INFO_RENDER %}
35 |
36 | {% if msg.discardable %}
× {% endif %}
37 |
{{msg.message}}
38 |
39 | {% endfor %}
40 | {% if settings.CAS_NEW_VERSION_HTML_WARNING and upgrade_available %}
41 |
42 |
×
43 |
{% blocktrans %}A new version of the application is available. This instance runs {{VERSION}} and the last version is {{LAST_VERSION}}. Please consider upgrading.{% endblocktrans %}
44 |
45 | {% endif %}
46 | {% block ante_messages %}{% endblock %}
47 | {% for message in messages %}
48 |
63 | {% endfor %}
64 | {% if auto_submit %} {% endif %}
65 | {% block content %}{% endblock %}
66 |
67 |
68 |
69 |
70 |
71 |
72 | {% if settings.CAS_SHOW_POWERED %}
73 |
76 | {% endif %}
77 |
78 |
79 |
80 |
91 | {% block javascript %}{% endblock %}
92 |
93 |
94 |
100 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist=
3 | flake8,
4 | check_rst,
5 | py3-django111,
6 | py3-django22,
7 | py3-django32,
8 | py3-django42,
9 | py3-django52,
10 |
11 | ##################
12 | # generic config #
13 | ##################
14 |
15 | [flake8]
16 | max-line-length=100
17 | exclude=migrations
18 | allowlist_externals={[post_cmd]allowlist_external}
19 |
20 | [base]
21 | deps =
22 | -r{toxinidir}/requirements-dev.txt
23 |
24 | [post_cmd]
25 | commands=
26 | find {toxworkdir} -name '*.pyc' -delete
27 | mkdir -p {toxinidir}/tox_logs/
28 | bash -c "mv {toxworkdir}/{envname}/log/* {toxinidir}/tox_logs/"
29 | allowlist_externals=
30 | find
31 | bash
32 | mkdir
33 |
34 | [testenv]
35 | setenv=
36 | PYTHONWARNINGS=always
37 | commands=
38 | py.test -rw {posargs:cas_server/tests/}
39 | {[post_cmd]commands}
40 | allowlist_externals={[post_cmd]allowlist_externals}
41 |
42 | ###################
43 | # genercic checks #
44 | ###################
45 |
46 | [testenv:flake8]
47 | basepython=python3
48 | deps=flake8
49 | skip_install=True
50 | commands=
51 | flake8 {toxinidir}/cas_server
52 | {[post_cmd]commands}
53 | allowlist_externals={[post_cmd]allowlist_externals}
54 |
55 | [testenv:check_rst]
56 | basepython=python3
57 | deps=
58 | docutils
59 | Pygments
60 | skip_install=True
61 | commands=
62 | rst2html --strict {toxinidir}/README.rst /dev/null
63 | rst2html --halt=warning {toxinidir}/CHANGELOG.rst /dev/null
64 | {[post_cmd]commands}
65 | allowlist_externals={[post_cmd]allowlist_externals}
66 |
67 | [testenv:coverage]
68 | basepython=python3
69 | passenv=
70 | COVERAGE_TOKEN
71 | CI_BUILD_REF_NAME
72 | TRAVIS_BRANCH
73 | TRAVIS_PULL_REQUEST
74 | deps=
75 | -r{toxinidir}/requirements.txt
76 | -r{toxinidir}/requirements-dev.txt
77 | skip_install=True
78 | commands=
79 | py.test --cov=cas_server --cov-report term --cov-report html
80 | git config --global --add safe.directory "{toxinidir}"
81 | {toxinidir}/.update_coverage "{toxinidir}" "django-cas-server"
82 | {[post_cmd]commands}
83 | allowlist_externals=
84 | {toxinidir}/.update_coverage
85 | git
86 | {[post_cmd]allowlist_externals}
87 |
88 |
89 | ####################
90 | # Python 2 support #
91 | ####################
92 |
93 | [testenv:py27-django111]
94 | basepython=python2.7
95 | deps =
96 | Django>=1.11,<1.12
97 | {[base]deps}
98 |
99 | ##################################
100 | # Generic Python 3 for local use #
101 | ##################################
102 |
103 | [testenv:py3-django111]
104 | basepython=python3
105 | deps =
106 | Django>=1.11,<1.12
107 | {[base]deps}
108 |
109 | [testenv:py3-django22]
110 | basepython=python3
111 | deps =
112 | Django>=2.2,<2.3
113 | {[base]deps}
114 |
115 | [testenv:py3-django32]
116 | basepython=python3
117 | deps =
118 | Django>=3.2,<3.3
119 | {[base]deps}
120 |
121 | [testenv:py3-django42]
122 | basepython=python3
123 | deps =
124 | Django>=4.2,<4.3
125 | {[base]deps}
126 |
127 | [testenv:py3-django52]
128 | basepython=python3
129 | deps =
130 | Django>=5.2,<5.3
131 | {[base]deps}
132 |
133 | #########################
134 | # Debian strech support #
135 | #########################
136 |
137 | [testenv:py35-django111]
138 | basepython=python3.5
139 | deps =
140 | Django>=1.11,<1.12
141 | {[base]deps}
142 |
143 | ####################################
144 | # Ubuntu bionic and EPEL 7 support #
145 | ####################################
146 |
147 | [testenv:py36-django111]
148 | basepython=python3.6
149 | deps =
150 | Django>=1.11,<1.12
151 | {[base]deps}
152 |
153 | ##################
154 | # RHEL 8 support #
155 | ##################
156 |
157 | [testenv:py36-django22]
158 | basepython=python3.6
159 | deps =
160 | Django>=2.2,<3.0
161 | {[base]deps}
162 |
163 | #########################
164 | # Debian buster support #
165 | #########################
166 |
167 | [testenv:py37-django111]
168 | basepython=python3.7
169 | deps =
170 | Django>=1.11,<1.12
171 | {[base]deps}
172 |
173 | ########################
174 | # Ubuntu focal support #
175 | ########################
176 |
177 | [testenv:py38-django22]
178 | basepython=python3.8
179 | deps =
180 | Django>=2.2,<3.0
181 | {[base]deps}
182 |
183 | ###################
184 | # Debian bullseye #
185 | ###################
186 |
187 | [testenv:py39-django22]
188 | basepython=python3.9
189 | deps =
190 | Django>=2.2,<3.0
191 | {[base]deps}
192 |
193 | ################
194 | # Ubuntu jammy #
195 | ################
196 |
197 | [testenv:py310-django32]
198 | basepython=python3.10
199 | deps =
200 | Django>=3.2,<3.3
201 | {[base]deps}
202 |
203 |
204 | ###########################
205 | # Debian bookworm support #
206 | ###########################
207 |
208 | [testenv:py311-django32]
209 | basepython=python3.11
210 | deps =
211 | Django>=3.2,<3.3
212 | {[base]deps}
213 |
214 |
215 | ################
216 | # Ubuntu noble #
217 | ################
218 |
219 | [testenv:py312-django42]
220 | basepython=python3.12
221 | deps =
222 | Django>=4.2,<4.3
223 | {[base]deps}
224 |
225 |
226 | #######################################
227 | # Django additional supported version #
228 | #######################################
229 |
230 | [testenv:py310-django52]
231 | basepython=python3.10
232 | deps =
233 | Django>=5.2,<5.3
234 | {[base]deps}
235 |
236 | [testenv:py311-django52]
237 | basepython=python3.11
238 | deps =
239 | Django>=5.2,<5.3
240 | {[base]deps}
241 |
242 | [testenv:py312-django52]
243 | basepython=python3.12
244 | deps =
245 | Django>=5.2,<5.3
246 | {[base]deps}
247 |
248 | [testenv:py313-django52]
249 | basepython=python3.13
250 | deps =
251 | Django>=5.2,<5.3
252 | {[base]deps}
253 |
--------------------------------------------------------------------------------
/cas_server/federate.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This program is distributed in the hope that it will be useful, but WITHOUT
3 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
5 | # more details.
6 | #
7 | # You should have received a copy of the GNU General Public License version 3
8 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
9 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
10 | #
11 | # (c) 2016-2025 Valentin Samir
12 | """federated mode helper classes"""
13 | from .default_settings import SessionStore
14 | from django.db import IntegrityError
15 |
16 | from .cas import CASClient
17 | from .models import FederatedUser, FederateSLO, User
18 |
19 | import logging
20 | import urllib
21 |
22 | #: logger facility
23 | logger = logging.getLogger(__name__)
24 |
25 |
26 | class CASFederateValidateUser(object):
27 | """
28 | Class CAS client used to authenticate the user again a CAS provider
29 |
30 | :param cas_server.models.FederatedIendityProvider provider: The provider to use for
31 | authenticate the user.
32 | :param unicode service_url: The service url to transmit to the ``provider``.
33 | """
34 | #: the provider returned username
35 | username = None
36 | #: the provider returned attributes
37 | attributs = {}
38 | #: the CAS client instance
39 | client = None
40 | #: the provider returned username this the provider suffix appended
41 | federated_username = None
42 | #: the identity provider
43 | provider = None
44 |
45 | def __init__(self, provider, service_url, renew=False):
46 | self.provider = provider
47 | self.client = CASClient(
48 | service_url=service_url,
49 | version=provider.cas_protocol_version,
50 | server_url=provider.server_url,
51 | renew=renew,
52 | )
53 |
54 | def get_login_url(self):
55 | """
56 | :return: the CAS provider login url
57 | :rtype: unicode
58 | """
59 | return self.client.get_login_url()
60 |
61 | def get_logout_url(self, redirect_url=None):
62 | """
63 | :param redirect_url: The url to redirect to after logout from the provider, if provided.
64 | :type redirect_url: :obj:`unicode` or :obj:`NoneType`
65 | :return: the CAS provider logout url
66 | :rtype: unicode
67 | """
68 | return self.client.get_logout_url(redirect_url)
69 |
70 | def verify_ticket(self, ticket):
71 | """
72 | test ``ticket`` against the CAS provider, if valid, create a
73 | :class:`FederatedUser` matching provider returned
74 | username and attributes.
75 |
76 | :param unicode ticket: The ticket to validate against the provider CAS
77 | :return: ``True`` if the validation succeed, else ``False``.
78 | :rtype: bool
79 | """
80 | try:
81 | username, attributs = self.client.verify_ticket(ticket)[:2]
82 | except urllib.error.URLError:
83 | return False
84 | if username is not None:
85 | if attributs is None:
86 | attributs = {}
87 | attributs["provider"] = self.provider.suffix
88 | self.username = username
89 | self.attributs = attributs
90 | user = FederatedUser.objects.update_or_create(
91 | username=username,
92 | provider=self.provider,
93 | defaults=dict(attributs=attributs, ticket=ticket)
94 | )[0]
95 | user.save()
96 | self.federated_username = user.federated_username
97 | return True
98 | else:
99 | return False
100 |
101 | @staticmethod
102 | def register_slo(username, session_key, ticket):
103 | """
104 | association a ``ticket`` with a (``username``, ``session_key``) for processing later SLO
105 | request by creating a :class:`cas_server.models.FederateSLO` object.
106 |
107 | :param unicode username: A logged user username, with the ``@`` component.
108 | :param unicode session_key: A logged user session_key matching ``username``.
109 | :param unicode ticket: A ticket used to authentication ``username`` for the session
110 | ``session_key``.
111 | """
112 | try:
113 | FederateSLO.objects.create(
114 | username=username,
115 | session_key=session_key,
116 | ticket=ticket
117 | )
118 | except IntegrityError: # pragma: no cover (ignore if the FederateSLO already exists)
119 | pass
120 |
121 | def clean_sessions(self, logout_request):
122 | """
123 | process a SLO request: Search for ticket values in ``logout_request``. For each
124 | ticket value matching a :class:`cas_server.models.FederateSLO`, disconnect the
125 | corresponding user.
126 |
127 | :param unicode logout_request: An XML document contening one or more Single Log Out
128 | requests.
129 | """
130 | try:
131 | slos = self.client.get_saml_slos(logout_request) or []
132 | except NameError: # pragma: no cover (should not happen)
133 | slos = []
134 | for slo in slos:
135 | for federate_slo in FederateSLO.objects.filter(ticket=slo.text):
136 | logger.info(
137 | "Got an SLO requests for ticket %s, logging out user %s" % (
138 | federate_slo.username,
139 | federate_slo.ticket
140 | )
141 | )
142 | session = SessionStore(session_key=federate_slo.session_key)
143 | session.flush()
144 | try:
145 | user = User.objects.get(
146 | username=federate_slo.username,
147 | session_key=federate_slo.session_key
148 | )
149 | user.logout()
150 | user.delete()
151 | except User.DoesNotExist: # pragma: no cover (should not happen)
152 | pass
153 | federate_slo.delete()
154 |
--------------------------------------------------------------------------------
/cas_server/admin.py:
--------------------------------------------------------------------------------
1 | # This program is distributed in the hope that it will be useful, but WITHOUT
2 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
3 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
4 | # more details.
5 | #
6 | # You should have received a copy of the GNU General Public License version 3
7 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
8 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
9 | #
10 | # (c) 2015-2024 Valentin Samir
11 | """module for the admin interface of the app"""
12 | from .default_settings import settings
13 |
14 | from django.contrib import admin
15 | from .models import ServiceTicket, ProxyTicket, ProxyGrantingTicket, User, ServicePattern
16 | from .models import Username, ReplaceAttributName, ReplaceAttributValue, FilterAttributValue
17 | from .models import FederatedIendityProvider, FederatedUser, UserAttributes
18 | from .utils import import_attr
19 |
20 |
21 | class BaseInlines(admin.TabularInline):
22 | """
23 | Bases: :class:`django.contrib.admin.TabularInline`
24 |
25 | Base class for inlines in the admin interface.
26 | """
27 | #: This controls the number of extra forms the formset will display in addition to
28 | #: the initial forms.
29 | extra = 0
30 |
31 |
32 | class UserAdminInlines(BaseInlines):
33 | """
34 | Bases: :class:`BaseInlines`
35 |
36 | Base class for inlines in :class:`UserAdmin` interface
37 | """
38 | #: The form :class:`TicketForm` used to display tickets.
39 | form = import_attr(settings.CAS_TICKET_FORM)
40 | #: Fields to display on a object that are read only (not editable).
41 | readonly_fields = (
42 | 'validate', 'service', 'service_pattern',
43 | 'creation', 'renew', 'single_log_out', 'value'
44 | )
45 | #: Fields to display on a object.
46 | fields = (
47 | 'validate', 'service', 'service_pattern',
48 | 'creation', 'renew', 'single_log_out'
49 | )
50 |
51 |
52 | class ServiceTicketInline(UserAdminInlines):
53 | """
54 | Bases: :class:`UserAdminInlines`
55 |
56 | :class:`ServiceTicket` in admin interface
57 | """
58 | #: The model which the inline is using.
59 | model = ServiceTicket
60 |
61 |
62 | class ProxyTicketInline(UserAdminInlines):
63 | """
64 | Bases: :class:`UserAdminInlines`
65 |
66 | :class:`ProxyTicket` in admin interface
67 | """
68 | #: The model which the inline is using.
69 | model = ProxyTicket
70 |
71 |
72 | class ProxyGrantingInline(UserAdminInlines):
73 | """
74 | Bases: :class:`UserAdminInlines`
75 |
76 | :class:`ProxyGrantingTicket` in admin interface
77 | """
78 | #: The model which the inline is using.
79 | model = ProxyGrantingTicket
80 |
81 |
82 | class UserAdmin(admin.ModelAdmin):
83 | """
84 | Bases: :class:`django.contrib.admin.ModelAdmin`
85 |
86 | :class:`User` in admin interface
87 | """
88 | #: See :class:`ServiceTicketInline`, :class:`ProxyTicketInline`, :class:`ProxyGrantingInline`
89 | #: objects below the :class:`UserAdmin` fields.
90 | inlines = (ServiceTicketInline, ProxyTicketInline, ProxyGrantingInline)
91 | #: Fields to display on a object that are read only (not editable).
92 | readonly_fields = ('username', 'date', "session_key")
93 | #: Fields to display on a object.
94 | fields = ('username', 'date', "session_key")
95 | #: Fields to display on the list of class:`UserAdmin` objects.
96 | list_display = ('username', 'date', "session_key")
97 |
98 |
99 | class UsernamesInline(BaseInlines):
100 | """
101 | Bases: :class:`BaseInlines`
102 |
103 | :class:`Username` in admin interface
104 | """
105 | #: The model which the inline is using.
106 | model = Username
107 |
108 |
109 | class ReplaceAttributNameInline(BaseInlines):
110 | """
111 | Bases: :class:`BaseInlines`
112 |
113 | :class:`ReplaceAttributName` in admin interface
114 | """
115 | #: The model which the inline is using.
116 | model = ReplaceAttributName
117 |
118 |
119 | class ReplaceAttributValueInline(BaseInlines):
120 | """
121 | Bases: :class:`BaseInlines`
122 |
123 | :class:`ReplaceAttributValue` in admin interface
124 | """
125 | #: The model which the inline is using.
126 | model = ReplaceAttributValue
127 |
128 |
129 | class FilterAttributValueInline(BaseInlines):
130 | """
131 | Bases: :class:`BaseInlines`
132 |
133 | :class:`FilterAttributValue` in admin interface
134 | """
135 | #: The model which the inline is using.
136 | model = FilterAttributValue
137 |
138 |
139 | class ServicePatternAdmin(admin.ModelAdmin):
140 | """
141 | Bases: :class:`django.contrib.admin.ModelAdmin`
142 |
143 | :class:`ServicePattern` in admin interface
144 | """
145 | #: See :class:`UsernamesInline`, :class:`ReplaceAttributNameInline`,
146 | #: :class:`ReplaceAttributValueInline`, :class:`FilterAttributValueInline` objects below
147 | #: the :class:`ServicePatternAdmin` fields.
148 | inlines = (
149 | UsernamesInline,
150 | ReplaceAttributNameInline,
151 | ReplaceAttributValueInline,
152 | FilterAttributValueInline
153 | )
154 | #: Fields to display on the list of class:`ServicePatternAdmin` objects.
155 | list_display = ('pos', 'name', 'pattern', 'proxy',
156 | 'single_log_out', 'proxy_callback', 'restrict_users')
157 |
158 |
159 | class FederatedIendityProviderAdmin(admin.ModelAdmin):
160 | """
161 | Bases: :class:`django.contrib.admin.ModelAdmin`
162 |
163 | :class:`FederatedIendityProvider` in admin
164 | interface
165 | """
166 | #: Fields to display on a object.
167 | fields = ('pos', 'suffix', 'server_url', 'cas_protocol_version', 'verbose_name', 'display')
168 | #: Fields to display on the list of class:`FederatedIendityProviderAdmin` objects.
169 | list_display = ('verbose_name', 'suffix', 'display')
170 |
171 |
172 | class FederatedUserAdmin(admin.ModelAdmin):
173 | """
174 | Bases: :class:`django.contrib.admin.ModelAdmin`
175 |
176 | :class:`FederatedUser` in admin
177 | interface
178 | """
179 | #: Fields to display on a object.
180 | fields = ('username', 'provider', 'last_update')
181 | #: Fields to display on the list of class:`FederatedUserAdmin` objects.
182 | list_display = ('username', 'provider', 'last_update')
183 |
184 |
185 | class UserAttributesAdmin(admin.ModelAdmin):
186 | """
187 | Bases: :class:`django.contrib.admin.ModelAdmin`
188 |
189 | :class:`UserAttributes` in admin
190 | interface
191 | """
192 | #: Fields to display on a object.
193 | fields = ('username', '_attributs')
194 |
195 |
196 | admin.site.register(ServicePattern, ServicePatternAdmin)
197 | admin.site.register(FederatedIendityProvider, FederatedIendityProviderAdmin)
198 | if settings.DEBUG: # pragma: no branch (we always test with DEBUG True)
199 | admin.site.register(User, UserAdmin)
200 | admin.site.register(FederatedUser, FederatedUserAdmin)
201 | admin.site.register(UserAttributes, UserAttributesAdmin)
202 |
--------------------------------------------------------------------------------
/cas_server/static/cas_server/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
22 |
24 |
46 |
48 |
49 |
51 | image/svg+xml
52 |
54 |
55 |
56 |
57 |
58 |
63 |
68 |
71 |
76 |
81 |
86 |
91 |
96 |
101 |
102 | CAS
113 |
114 |
115 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # Internal variables.
11 | PAPEROPT_a4 = -D latex_paper_size=a4
12 | PAPEROPT_letter = -D latex_paper_size=letter
13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
14 | # the i18n builder cannot share the environment and doctrees with the others
15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
16 |
17 | .PHONY: help
18 | help:
19 | @echo "Please use \`make ' where is one of"
20 | @echo " html to make standalone HTML files"
21 | @echo " dirhtml to make HTML files named index.html in directories"
22 | @echo " singlehtml to make a single large HTML file"
23 | @echo " pickle to make pickle files"
24 | @echo " json to make JSON files"
25 | @echo " htmlhelp to make HTML files and a HTML help project"
26 | @echo " qthelp to make HTML files and a qthelp project"
27 | @echo " applehelp to make an Apple Help Book"
28 | @echo " devhelp to make HTML files and a Devhelp project"
29 | @echo " epub to make an epub"
30 | @echo " epub3 to make an epub3"
31 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
32 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
33 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
34 | @echo " text to make text files"
35 | @echo " man to make manual pages"
36 | @echo " texinfo to make Texinfo files"
37 | @echo " info to make Texinfo files and run them through makeinfo"
38 | @echo " gettext to make PO message catalogs"
39 | @echo " changes to make an overview of all changed/added/deprecated items"
40 | @echo " xml to make Docutils-native XML files"
41 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
42 | @echo " linkcheck to check all external links for integrity"
43 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
44 | @echo " coverage to run coverage check of the documentation (if enabled)"
45 | @echo " dummy to check syntax errors of document sources"
46 |
47 | .PHONY: clean
48 | clean:
49 | rm -rf $(BUILDDIR)/*
50 |
51 | .PHONY: html
52 | html:
53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
54 | @echo
55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
56 |
57 | .PHONY: dirhtml
58 | dirhtml:
59 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
60 | @echo
61 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
62 |
63 | .PHONY: singlehtml
64 | singlehtml:
65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
66 | @echo
67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
68 |
69 | .PHONY: pickle
70 | pickle:
71 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
72 | @echo
73 | @echo "Build finished; now you can process the pickle files."
74 |
75 | .PHONY: json
76 | json:
77 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
78 | @echo
79 | @echo "Build finished; now you can process the JSON files."
80 |
81 | .PHONY: htmlhelp
82 | htmlhelp:
83 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
84 | @echo
85 | @echo "Build finished; now you can run HTML Help Workshop with the" \
86 | ".hhp project file in $(BUILDDIR)/htmlhelp."
87 |
88 | .PHONY: qthelp
89 | qthelp:
90 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
91 | @echo
92 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
93 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
94 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-cas-server.qhcp"
95 | @echo "To view the help file:"
96 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-cas-server.qhc"
97 |
98 | .PHONY: applehelp
99 | applehelp:
100 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
101 | @echo
102 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
103 | @echo "N.B. You won't be able to view it unless you put it in" \
104 | "~/Library/Documentation/Help or install it in your application" \
105 | "bundle."
106 |
107 | .PHONY: devhelp
108 | devhelp:
109 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
110 | @echo
111 | @echo "Build finished."
112 | @echo "To view the help file:"
113 | @echo "# mkdir -p $$HOME/.local/share/devhelp/django-cas-server"
114 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-cas-server"
115 | @echo "# devhelp"
116 |
117 | .PHONY: epub
118 | epub:
119 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
120 | @echo
121 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
122 |
123 | .PHONY: epub3
124 | epub3:
125 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
126 | @echo
127 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
128 |
129 | .PHONY: latex
130 | latex:
131 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
132 | @echo
133 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
134 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
135 | "(use \`make latexpdf' here to do that automatically)."
136 |
137 | .PHONY: latexpdf
138 | latexpdf:
139 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
140 | @echo "Running LaTeX files through pdflatex..."
141 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
142 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
143 |
144 | .PHONY: latexpdfja
145 | latexpdfja:
146 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
147 | @echo "Running LaTeX files through platex and dvipdfmx..."
148 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
149 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
150 |
151 | .PHONY: text
152 | text:
153 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
154 | @echo
155 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
156 |
157 | .PHONY: man
158 | man:
159 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
160 | @echo
161 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
162 |
163 | .PHONY: texinfo
164 | texinfo:
165 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
166 | @echo
167 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
168 | @echo "Run \`make' in that directory to run these through makeinfo" \
169 | "(use \`make info' here to do that automatically)."
170 |
171 | .PHONY: info
172 | info:
173 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
174 | @echo "Running Texinfo files through makeinfo..."
175 | make -C $(BUILDDIR)/texinfo info
176 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
177 |
178 | .PHONY: gettext
179 | gettext:
180 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
181 | @echo
182 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
183 |
184 | .PHONY: changes
185 | changes:
186 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
187 | @echo
188 | @echo "The overview file is in $(BUILDDIR)/changes."
189 |
190 | .PHONY: linkcheck
191 | linkcheck:
192 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
193 | @echo
194 | @echo "Link check complete; look for any errors in the above output " \
195 | "or in $(BUILDDIR)/linkcheck/output.txt."
196 |
197 | .PHONY: doctest
198 | doctest:
199 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
200 | @echo "Testing of doctests in the sources finished, look at the " \
201 | "results in $(BUILDDIR)/doctest/output.txt."
202 |
203 | .PHONY: coverage
204 | coverage:
205 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
206 | @echo "Testing of coverage in the sources finished, look at the " \
207 | "results in $(BUILDDIR)/coverage/python.txt."
208 |
209 | .PHONY: xml
210 | xml:
211 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
212 | @echo
213 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
214 |
215 | .PHONY: pseudoxml
216 | pseudoxml:
217 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
218 | @echo
219 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
220 |
221 | .PHONY: dummy
222 | dummy:
223 | $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
224 | @echo
225 | @echo "Build finished. Dummy builder generates no files."
226 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. epub3 to make an epub3
31 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
32 | echo. text to make text files
33 | echo. man to make manual pages
34 | echo. texinfo to make Texinfo files
35 | echo. gettext to make PO message catalogs
36 | echo. changes to make an overview over all changed/added/deprecated items
37 | echo. xml to make Docutils-native XML files
38 | echo. pseudoxml to make pseudoxml-XML files for display purposes
39 | echo. linkcheck to check all external links for integrity
40 | echo. doctest to run all doctests embedded in the documentation if enabled
41 | echo. coverage to run coverage check of the documentation if enabled
42 | echo. dummy to check syntax errors of document sources
43 | goto end
44 | )
45 |
46 | if "%1" == "clean" (
47 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
48 | del /q /s %BUILDDIR%\*
49 | goto end
50 | )
51 |
52 |
53 | REM Check if sphinx-build is available and fallback to Python version if any
54 | %SPHINXBUILD% 1>NUL 2>NUL
55 | if errorlevel 9009 goto sphinx_python
56 | goto sphinx_ok
57 |
58 | :sphinx_python
59 |
60 | set SPHINXBUILD=python -m sphinx.__init__
61 | %SPHINXBUILD% 2> nul
62 | if errorlevel 9009 (
63 | echo.
64 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
65 | echo.installed, then set the SPHINXBUILD environment variable to point
66 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
67 | echo.may add the Sphinx directory to PATH.
68 | echo.
69 | echo.If you don't have Sphinx installed, grab it from
70 | echo.http://sphinx-doc.org/
71 | exit /b 1
72 | )
73 |
74 | :sphinx_ok
75 |
76 |
77 | if "%1" == "html" (
78 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
79 | if errorlevel 1 exit /b 1
80 | echo.
81 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
82 | goto end
83 | )
84 |
85 | if "%1" == "dirhtml" (
86 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
87 | if errorlevel 1 exit /b 1
88 | echo.
89 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
90 | goto end
91 | )
92 |
93 | if "%1" == "singlehtml" (
94 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
95 | if errorlevel 1 exit /b 1
96 | echo.
97 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
98 | goto end
99 | )
100 |
101 | if "%1" == "pickle" (
102 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
103 | if errorlevel 1 exit /b 1
104 | echo.
105 | echo.Build finished; now you can process the pickle files.
106 | goto end
107 | )
108 |
109 | if "%1" == "json" (
110 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
111 | if errorlevel 1 exit /b 1
112 | echo.
113 | echo.Build finished; now you can process the JSON files.
114 | goto end
115 | )
116 |
117 | if "%1" == "htmlhelp" (
118 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
119 | if errorlevel 1 exit /b 1
120 | echo.
121 | echo.Build finished; now you can run HTML Help Workshop with the ^
122 | .hhp project file in %BUILDDIR%/htmlhelp.
123 | goto end
124 | )
125 |
126 | if "%1" == "qthelp" (
127 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
128 | if errorlevel 1 exit /b 1
129 | echo.
130 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
131 | .qhcp project file in %BUILDDIR%/qthelp, like this:
132 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-cas-server.qhcp
133 | echo.To view the help file:
134 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-cas-server.ghc
135 | goto end
136 | )
137 |
138 | if "%1" == "devhelp" (
139 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
140 | if errorlevel 1 exit /b 1
141 | echo.
142 | echo.Build finished.
143 | goto end
144 | )
145 |
146 | if "%1" == "epub" (
147 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
148 | if errorlevel 1 exit /b 1
149 | echo.
150 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
151 | goto end
152 | )
153 |
154 | if "%1" == "epub3" (
155 | %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
156 | if errorlevel 1 exit /b 1
157 | echo.
158 | echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
159 | goto end
160 | )
161 |
162 | if "%1" == "latex" (
163 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
164 | if errorlevel 1 exit /b 1
165 | echo.
166 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
167 | goto end
168 | )
169 |
170 | if "%1" == "latexpdf" (
171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
172 | cd %BUILDDIR%/latex
173 | make all-pdf
174 | cd %~dp0
175 | echo.
176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
177 | goto end
178 | )
179 |
180 | if "%1" == "latexpdfja" (
181 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
182 | cd %BUILDDIR%/latex
183 | make all-pdf-ja
184 | cd %~dp0
185 | echo.
186 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
187 | goto end
188 | )
189 |
190 | if "%1" == "text" (
191 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
192 | if errorlevel 1 exit /b 1
193 | echo.
194 | echo.Build finished. The text files are in %BUILDDIR%/text.
195 | goto end
196 | )
197 |
198 | if "%1" == "man" (
199 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
200 | if errorlevel 1 exit /b 1
201 | echo.
202 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
203 | goto end
204 | )
205 |
206 | if "%1" == "texinfo" (
207 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
208 | if errorlevel 1 exit /b 1
209 | echo.
210 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
211 | goto end
212 | )
213 |
214 | if "%1" == "gettext" (
215 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
216 | if errorlevel 1 exit /b 1
217 | echo.
218 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
219 | goto end
220 | )
221 |
222 | if "%1" == "changes" (
223 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
224 | if errorlevel 1 exit /b 1
225 | echo.
226 | echo.The overview file is in %BUILDDIR%/changes.
227 | goto end
228 | )
229 |
230 | if "%1" == "linkcheck" (
231 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
232 | if errorlevel 1 exit /b 1
233 | echo.
234 | echo.Link check complete; look for any errors in the above output ^
235 | or in %BUILDDIR%/linkcheck/output.txt.
236 | goto end
237 | )
238 |
239 | if "%1" == "doctest" (
240 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
241 | if errorlevel 1 exit /b 1
242 | echo.
243 | echo.Testing of doctests in the sources finished, look at the ^
244 | results in %BUILDDIR%/doctest/output.txt.
245 | goto end
246 | )
247 |
248 | if "%1" == "coverage" (
249 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
250 | if errorlevel 1 exit /b 1
251 | echo.
252 | echo.Testing of coverage in the sources finished, look at the ^
253 | results in %BUILDDIR%/coverage/python.txt.
254 | goto end
255 | )
256 |
257 | if "%1" == "xml" (
258 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
259 | if errorlevel 1 exit /b 1
260 | echo.
261 | echo.Build finished. The XML files are in %BUILDDIR%/xml.
262 | goto end
263 | )
264 |
265 | if "%1" == "pseudoxml" (
266 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
267 | if errorlevel 1 exit /b 1
268 | echo.
269 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
270 | goto end
271 | )
272 |
273 | if "%1" == "dummy" (
274 | %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
275 | if errorlevel 1 exit /b 1
276 | echo.
277 | echo.Build finished. Dummy builder generates no files.
278 | goto end
279 | )
280 |
281 | :end
282 |
--------------------------------------------------------------------------------
/cas_server/forms.py:
--------------------------------------------------------------------------------
1 | # This program is distributed in the hope that it will be useful, but WITHOUT
2 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
3 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
4 | # more details.
5 | #
6 | # You should have received a copy of the GNU General Public License version 3
7 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
8 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
9 | #
10 | # (c) 2015-2020 Valentin Samir
11 | """forms for the app"""
12 | from .default_settings import settings
13 |
14 | from django import forms
15 | from django.forms import widgets
16 |
17 | import cas_server.utils as utils
18 | import cas_server.models as models
19 |
20 | import sys
21 | if sys.version_info < (3, ):
22 | from django.utils.translation import ugettext_lazy as _
23 | else:
24 | from django.utils.translation import gettext_lazy as _
25 |
26 |
27 | class BootsrapForm(forms.Form):
28 | """
29 | Bases: :class:`django.forms.Form`
30 |
31 | Form base class to use boostrap then rendering the form fields
32 | """
33 | def __init__(self, *args, **kwargs):
34 | super(BootsrapForm, self).__init__(*args, **kwargs)
35 | for field in self.fields.values():
36 | # Only tweak the field if it will be displayed
37 | if not isinstance(field.widget, widgets.HiddenInput):
38 | attrs = {}
39 | if (
40 | isinstance(field.widget, (widgets.Input, widgets.Select, widgets.Textarea)) and
41 | not isinstance(field.widget, (widgets.CheckboxInput,))
42 | ):
43 | attrs['class'] = "form-control"
44 | if isinstance(field.widget, (widgets.Input, widgets.Textarea)) and field.label:
45 | attrs["placeholder"] = field.label
46 | if field.required:
47 | attrs["required"] = "required"
48 | field.widget.attrs.update(attrs)
49 |
50 |
51 | class BaseLogin(BootsrapForm):
52 | """
53 | Bases: :class:`BootsrapForm`
54 |
55 | Base form with all field possibly hidden on the login pages
56 | """
57 | #: The service url for which the user want a ticket
58 | service = forms.CharField(widget=forms.HiddenInput(), required=False)
59 | #: A valid LoginTicket to prevent POST replay
60 | lt = forms.CharField(widget=forms.HiddenInput(), required=False)
61 | #: Is the service asking the authentication renewal ?
62 | renew = forms.BooleanField(widget=forms.HiddenInput(), required=False)
63 | #: Url to redirect to if the authentication fail (user not authenticated or bad service)
64 | gateway = forms.CharField(widget=forms.HiddenInput(), required=False)
65 | method = forms.CharField(widget=forms.HiddenInput(), required=False)
66 |
67 |
68 | class WarnForm(BaseLogin):
69 | """
70 | Bases: :class:`BaseLogin`
71 |
72 | Form used on warn page before emiting a ticket
73 | """
74 | #: ``True`` if the user has been warned of the ticket emission
75 | warned = forms.BooleanField(widget=forms.HiddenInput(), required=False)
76 |
77 |
78 | class FederateSelect(BaseLogin):
79 | """
80 | Bases: :class:`BaseLogin`
81 |
82 | Form used on the login page when ``settings.CAS_FEDERATE`` is ``True``
83 | allowing the user to choose an identity provider.
84 | """
85 | #: The providers the user can choose to be used as authentication backend
86 | provider = forms.ModelChoiceField(
87 | queryset=models.FederatedIendityProvider.objects.filter(display=True).order_by(
88 | "pos",
89 | "verbose_name",
90 | "suffix"
91 | ),
92 | to_field_name="suffix",
93 | label=_('Identity provider'),
94 | )
95 | #: A checkbox to ask to be warn before emiting a ticket for another service
96 | warn = forms.BooleanField(
97 | label=_('Warn me before logging me into other sites.'),
98 | required=False
99 | )
100 | #: A checkbox to remember the user choices of :attr:`provider`
101 | remember = forms.BooleanField(label=_('Remember the identity provider'), required=False)
102 |
103 |
104 | class UserCredential(BaseLogin):
105 | """
106 | Bases: :class:`BaseLogin`
107 |
108 | Form used on the login page to retrive user credentials
109 | """
110 | #: The user username
111 | username = forms.CharField(
112 | label=_('username'),
113 | widget=forms.TextInput(attrs={'autofocus': 'autofocus'})
114 | )
115 | #: The user password
116 | password = forms.CharField(label=_('password'), widget=forms.PasswordInput)
117 | #: A checkbox to ask to be warn before emiting a ticket for another service
118 | warn = forms.BooleanField(
119 | label=_('Warn me before logging me into other sites.'),
120 | required=False
121 | )
122 |
123 | def clean(self):
124 | """
125 | Validate that the submited :attr:`username` and :attr:`password` are valid
126 |
127 | :raises django.forms.ValidationError: if the :attr:`username` and :attr:`password`
128 | are not valid.
129 | :return: The cleaned POST data
130 | :rtype: dict
131 | """
132 | cleaned_data = super(UserCredential, self).clean()
133 | if "username" in cleaned_data and "password" in cleaned_data:
134 | auth = utils.import_attr(settings.CAS_AUTH_CLASS)(cleaned_data["username"])
135 | if auth.test_password(cleaned_data["password"]):
136 | cleaned_data["username"] = auth.username
137 | else:
138 | raise forms.ValidationError(
139 | _(u"The credentials you provided cannot be determined to be authentic.")
140 | )
141 | return cleaned_data
142 |
143 |
144 | class FederateUserCredential(UserCredential):
145 | """
146 | Bases: :class:`UserCredential`
147 |
148 | Form used on a auto submited page for linking the views
149 | :class:`FederateAuth` and
150 | :class:`LoginView`.
151 |
152 | On successful authentication on a provider, in the view
153 | :class:`FederateAuth` a
154 | :class:`FederatedUser` is created by
155 | :meth:`cas_server.federate.CASFederateValidateUser.verify_ticket` and the user is redirected
156 | to :class:`LoginView`. This form is then automatically filled
157 | with infos matching the created :class:`FederatedUser`
158 | using the ``ticket`` as one time password and submited using javascript. If javascript is
159 | not enabled, a connect button is displayed.
160 |
161 | This stub authentication form, allow to implement the federated mode with very few
162 | modificatons to the :class:`LoginView` view.
163 | """
164 |
165 | def __init__(self, *args, **kwargs):
166 | super(FederateUserCredential, self).__init__(*args, **kwargs)
167 | # All fields are hidden and auto filled by the /login view logic
168 | for name, field in self.fields.items():
169 | field.widget = forms.HiddenInput()
170 | self[name].display = False
171 |
172 | def clean(self):
173 | """
174 | Validate that the submited :attr:`username` and :attr:`password` are valid using
175 | the :class:`CASFederateAuth` auth class.
176 |
177 | :raises django.forms.ValidationError: if the :attr:`username` and :attr:`password`
178 | do not correspond to a :class:`FederatedUser`.
179 | :return: The cleaned POST data
180 | :rtype: dict
181 | """
182 | cleaned_data = super(FederateUserCredential, self).clean()
183 | try:
184 | user = models.FederatedUser.get_from_federated_username(cleaned_data["username"])
185 | user.ticket = ""
186 | user.save()
187 | # should not happed as if the FederatedUser do not exists, super should
188 | # raise before a ValidationError("bad user")
189 | except models.FederatedUser.DoesNotExist: # pragma: no cover (should not happend)
190 | raise forms.ValidationError(
191 | _(u"User not found in the temporary database, please try to reconnect")
192 | )
193 | return cleaned_data
194 |
195 |
196 | class TicketForm(forms.ModelForm):
197 | """
198 | Bases: :class:`django.forms.ModelForm`
199 |
200 | Form for Tickets in the admin interface
201 | """
202 | class Meta:
203 | model = models.Ticket
204 | exclude = []
205 | service = forms.CharField(label=_('service'), widget=forms.TextInput)
206 |
--------------------------------------------------------------------------------
/cas_server/locale/zh_Hans/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2021-08-01 22:18+0800\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=UTF-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 | "Plural-Forms: nplurals=1; plural=0;\n"
20 |
21 | #: apps.py:30 templates/cas_server/bs3/base.html:7
22 | #: templates/cas_server/bs3/base.html:26 templates/cas_server/bs4/base.html:6
23 | #: templates/cas_server/bs4/base.html:17
24 | msgid "Central Authentication Service"
25 | msgstr "认证中心服务"
26 |
27 | #: default_settings.py:230
28 | msgid ""
29 | "The Central Authentication Service grants you access to most of our websites "
30 | "by authenticating only once, so you don't need to type your credentials "
31 | "again unless your session expires or you logout."
32 | msgstr ""
33 | "您仅需在认证中心认证一次,就可以访问您的多数网站, "
34 | "这样您不再需要重复输入认证,除非您的会话过期,或者您登出了."
35 |
36 | #: forms.py:93
37 | msgid "Identity provider"
38 | msgstr "身份提供者"
39 |
40 | #: forms.py:97 forms.py:119
41 | msgid "Warn me before logging me into other sites."
42 | msgstr "登录到其它网站时警告我"
43 |
44 | #: forms.py:101
45 | msgid "Remember the identity provider"
46 | msgstr "记住此身份提供者"
47 |
48 | #: forms.py:112 models.py:646
49 | msgid "username"
50 | msgstr "用户名"
51 |
52 | #: forms.py:116
53 | msgid "password"
54 | msgstr "密码"
55 |
56 | #: forms.py:139
57 | msgid "The credentials you provided cannot be determined to be authentic."
58 | msgstr "您提供的令牌不能通过鉴权"
59 |
60 | #: forms.py:191
61 | msgid "User not found in the temporary database, please try to reconnect"
62 | msgstr "在临时数据库找不到此用户,请尝试重新连接"
63 |
64 | #: forms.py:205
65 | msgid "service"
66 | msgstr "服务"
67 |
68 | #: management/commands/cas_clean_federate.py:25
69 | msgid "Clean old federated users"
70 | msgstr "清除过期联盟用户"
71 |
72 | #: management/commands/cas_clean_sessions.py:27
73 | msgid "Clean deleted sessions"
74 | msgstr "清除被删除的会话"
75 |
76 | #: management/commands/cas_clean_tickets.py:27
77 | msgid "Clean old tickets"
78 | msgstr "清除过期凭证"
79 |
80 | #: models.py:79
81 | msgid "identity provider"
82 | msgstr "身份提供者"
83 |
84 | #: models.py:80
85 | msgid "identity providers"
86 | msgstr "身份证供者"
87 |
88 | #: models.py:86
89 | msgid "suffix"
90 | msgstr "后缀"
91 |
92 | #: models.py:88
93 | msgid ""
94 | "Suffix append to backend CAS returned username: ``returned_username`` @ "
95 | "``suffix``."
96 | msgstr "后端 CAS 附加后缀返回的用户名: ``returned_username`` @ "
97 | "``suffix``."
98 |
99 | #: models.py:95
100 | msgid "server url"
101 | msgstr "服务 url"
102 |
103 | #: models.py:105
104 | msgid "CAS protocol version"
105 | msgstr "CAS 协议版本"
106 |
107 | #: models.py:107
108 | msgid ""
109 | "Version of the CAS protocol to use when sending requests the the backend CAS."
110 | msgstr ""
111 | "后端 CAS 发送请求时使用的 CAS 协议版本"
112 |
113 | #: models.py:114
114 | msgid "verbose name"
115 | msgstr "详细名称"
116 |
117 | #: models.py:115
118 | msgid "Name for this identity provider displayed on the login page."
119 | msgstr "在登录页显示的身份提供者的名字"
120 |
121 | #: models.py:121 models.py:498
122 | msgid "position"
123 | msgstr "位置"
124 |
125 | #: models.py:135
126 | msgid "display"
127 | msgstr "显示"
128 |
129 | #: models.py:136
130 | msgid "Display the provider on the login page."
131 | msgstr "在登录页显示提供者"
132 |
133 | #: models.py:174
134 | msgid "Federated user"
135 | msgstr "联盟用户"
136 |
137 | #: models.py:175
138 | msgid "Federated users"
139 | msgstr "联盟用户"
140 |
141 | #: models.py:254
142 | msgid "User attributes cache"
143 | msgstr "用户属性缓存"
144 |
145 | #: models.py:255
146 | msgid "User attributes caches"
147 | msgstr "用户属性缓存"
148 |
149 | #: models.py:279
150 | msgid "User"
151 | msgstr "用户"
152 |
153 | #: models.py:280
154 | msgid "Users"
155 | msgstr "用户"
156 |
157 | #: models.py:372
158 | #, python-format
159 | msgid "Error during service logout %s"
160 | msgstr "服务登出中的异常 %s"
161 |
162 | #: models.py:492
163 | msgid "Service pattern"
164 | msgstr "服务范式"
165 |
166 | #: models.py:493
167 | msgid "Services patterns"
168 | msgstr "服务范式"
169 |
170 | #: models.py:499
171 | msgid "service patterns are sorted using the position attribute"
172 | msgstr "服务范式会按照位置属性排序"
173 |
174 | #: models.py:507 models.py:676
175 | msgid "name"
176 | msgstr "名称"
177 |
178 | #: models.py:508
179 | msgid "A name for the service"
180 | msgstr "服务的名称"
181 |
182 | #: models.py:516 models.py:723 models.py:757
183 | msgid "pattern"
184 | msgstr "范式"
185 |
186 | #: models.py:518
187 | msgid ""
188 | "A regular expression matching services. Will usually looks like '^https://"
189 | "some\\.server\\.com/path/.*$'.As it is a regular expression, special "
190 | "character must be escaped with a '\\'."
191 | msgstr ""
192 | "用一个正则表示式来匹配服务。一般如 '^https://"
193 | "some\\.server\\.com/path/.*$'. 在正则表达式中,特殊"
194 | "字符必须用 '\\' 转码."
195 |
196 | #: models.py:529
197 | msgid "user field"
198 | msgstr "用户字段"
199 |
200 | #: models.py:530
201 | msgid "Name of the attribute to transmit as username, empty = login"
202 | msgstr "被转译作为用户名的属性字段,空 = login"
203 |
204 | #: models.py:535
205 | msgid "restrict username"
206 | msgstr "用户名限制"
207 |
208 | #: models.py:536
209 | msgid "Limit username allowed to connect to the list provided bellow"
210 | msgstr "只允许下面列表提供的用户名连接"
211 |
212 | #: models.py:541
213 | msgid "proxy"
214 | msgstr "代理"
215 |
216 | #: models.py:542
217 | msgid "Proxy tickets can be delivered to the service"
218 | msgstr "可以对服务分发的代理凭证"
219 |
220 | #: models.py:548
221 | msgid "proxy callback"
222 | msgstr "代理回调"
223 |
224 | #: models.py:549
225 | msgid "can be used as a proxy callback to deliver PGT"
226 | msgstr "可以作为代理回调来分发PGT"
227 |
228 | #: models.py:556
229 | msgid "single log out"
230 | msgstr "单点登出"
231 |
232 | #: models.py:557
233 | msgid "Enable SLO for the service"
234 | msgstr "为服务启用 SLO"
235 |
236 | #: models.py:565
237 | msgid "single log out callback"
238 | msgstr "单点登出回调"
239 |
240 | #: models.py:566
241 | msgid ""
242 | "URL where the SLO request will be POST. empty = service url\n"
243 | "This is usefull for non HTTP proxied services."
244 | msgstr ""
245 | "SLO 的 POST 请求使用的 URL. 空 = 服务地址\n"
246 | "在为非 HTTP 代理服务时有用"
247 |
248 | #: models.py:647
249 | msgid "username allowed to connect to the service"
250 | msgstr "允许连接到服务的用户名"
251 |
252 | #: models.py:677
253 | msgid "name of an attribute to send to the service, use * for all attributes"
254 | msgstr "发给服务的属性名, 使用 * 表示所有属性"
255 |
256 | #: models.py:684 models.py:765
257 | msgid "replace"
258 | msgstr "替换"
259 |
260 | #: models.py:685
261 | msgid ""
262 | "name under which the attribute will be show to the service. empty = default "
263 | "name of the attribut"
264 | msgstr ""
265 | "展示给服务的属性的名字. 空 = default"
266 | "属性的名字"
267 |
268 | #: models.py:716 models.py:751
269 | msgid "attribute"
270 | msgstr "属性"
271 |
272 | #: models.py:717
273 | msgid "Name of the attribute which must verify pattern"
274 | msgstr "必须校验范式的属性的名字"
275 |
276 | #: models.py:724
277 | msgid "a regular expression"
278 | msgstr "一个正则表达式"
279 |
280 | #: models.py:752
281 | msgid "Name of the attribute for which the value must be replace"
282 | msgstr "必须被替换的值的属性的名字"
283 |
284 | #: models.py:758
285 | msgid "An regular expression maching whats need to be replaced"
286 | msgstr "一个正则表达式,符合的将要被替换"
287 |
288 | #: models.py:766
289 | msgid "replace expression, groups are capture by \\1, \\2 …"
290 | msgstr "替换表达式, 用 \\`, \\2 等等来替换组"
291 |
292 | #: templates/cas_server/bs3/base.html:43 templates/cas_server/bs4/base.html:28
293 | #, python-format
294 | msgid ""
295 | "A new version of the application is available. This instance runs "
296 | "%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
297 | "upgrading."
298 | msgstr ""
299 | "此应用有一个新版本可用. 此实例运行于 %(VERSION)s, 最新的版本是 %(LAST_VERSION)s. 请考虑升级"
300 |
301 | #: templates/cas_server/bs3/logged.html:4
302 | #: templates/cas_server/bs4/logged.html:4
303 | msgid ""
304 | "Log In Successful You have successfully logged into the Central "
305 | "Authentication Service. For security reasons, please Log Out and Exit "
306 | "your web browser when you are done accessing services that require "
307 | "authentication!"
308 | msgstr ""
309 | "登入成功 您已经成功登入认证中心."
310 | " 出于安全考虑, 当您用完需要认证的服务时,请您登出并退出您的浏览器!"
311 |
312 | #: templates/cas_server/bs3/logged.html:8
313 | #: templates/cas_server/bs4/logged.html:8
314 | msgid "Log me out from all my sessions"
315 | msgstr "从我的所有会话中登出"
316 |
317 | #: templates/cas_server/bs3/logged.html:14
318 | #: templates/cas_server/bs4/logged.html:14
319 | msgid "Forget the identity provider"
320 | msgstr "忘掉身份提供者"
321 |
322 | #: templates/cas_server/bs3/logged.html:18
323 | #: templates/cas_server/bs4/logged.html:18
324 | msgid "Logout"
325 | msgstr "登出"
326 |
327 | #: templates/cas_server/bs3/login.html:6 templates/cas_server/bs4/login.html:7
328 | msgid "Please log in"
329 | msgstr "请登录"
330 |
331 | #: templates/cas_server/bs3/login.html:14
332 | #: templates/cas_server/bs4/login.html:17
333 | msgid "Login"
334 | msgstr "登录"
335 |
336 | #: templates/cas_server/bs3/warn.html:9 templates/cas_server/bs4/warn.html:9
337 | msgid "Connect to the service"
338 | msgstr "连接到服务"
339 |
340 | #: utils.py:753
341 | #, python-format
342 | msgid "\"%(value)s\" is not a valid regular expression"
343 | msgstr "\"%(value)s\" 不是一个有效的正则表达式"
344 |
345 | #: views.py:197
346 | msgid ""
347 | "Logout successful You have successfully logged out from the Central "
348 | "Authentication Service. For security reasons, close your web browser."
349 | msgstr ""
350 | "登出成功 您成功从认证中心登出."
351 | "安全起见,请关闭您的浏览器"
352 |
353 |
354 | #: views.py:203
355 | #, python-format
356 | msgid ""
357 | "Logout successful You have successfully logged out from %d sessions "
358 | "of the Central Authentication Service. For security reasons, close your web "
359 | "browser."
360 | msgstr ""
361 | "登出成功 您已经从认证中心服务的会话 %d 中成功登出"
362 | "为安全起见,请关闭您的浏览器"
363 |
364 | #: views.py:210
365 | msgid ""
366 | "Logout successful You were already logged out from the Central "
367 | "Authentication Service. For security reasons, close your web browser."
368 | msgstr ""
369 | "登出成功 您已经从认证中心服务登出. "
370 | "为安全起见,请关闭您的浏览器"
371 |
372 | #: views.py:391
373 | #, python-format
374 | msgid ""
375 | "Invalid response from your identity provider CAS upon ticket %(ticket)s "
376 | "validation: %(error)r"
377 | msgstr ""
378 | "您的身份提供者 CAS 对凭证 %(ticket)s 返回了无效响应"
379 | "校验: %(error)r"
380 |
381 | #: views.py:513
382 | msgid "Invalid login ticket, please try to log in again"
383 | msgstr "无效登录凭证, 请尝试重新登录"
384 |
385 | #: views.py:706
386 | #, python-format
387 | msgid "Authentication has been required by service %(name)s (%(url)s)"
388 | msgstr "服务 %(name)s (%(url)s) 需要认证"
389 |
390 | #: views.py:744
391 | #, python-format
392 | msgid "Service %(url)s not allowed."
393 | msgstr "不允许的服务 %(url)s"
394 |
395 | #: views.py:751
396 | msgid "Username not allowed"
397 | msgstr "不允许的用户名"
398 |
399 | #: views.py:758
400 | msgid "User characteristics not allowed"
401 | msgstr "不允许的用户特征"
402 |
403 | #: views.py:765
404 | #, python-format
405 | msgid "The attribute %(field)s is needed to use that service"
406 | msgstr "使用那个服务需要属性 %(field)s"
407 |
408 | #: views.py:857
409 | #, python-format
410 | msgid "Authentication renewal required by service %(name)s (%(url)s)."
411 | msgstr "服务 %(name)s (%(url)s) 需要更新认证"
412 |
413 | #: views.py:864
414 | #, python-format
415 | msgid "Authentication required by service %(name)s (%(url)s)."
416 | msgstr "服务 %(name)s (%(url)s) 需要认证."
417 |
418 | #: views.py:872
419 | #, python-format
420 | msgid "Service %s not allowed"
421 | msgstr "不允许的服务 %s"
422 |
--------------------------------------------------------------------------------
/cas_server/tests/mixin.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This program is distributed in the hope that it will be useful, but WITHOUT
3 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
5 | # more details.
6 | #
7 | # You should have received a copy of the GNU General Public License version 3
8 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
9 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
10 | #
11 | # (c) 2016-2019 Valentin Samir
12 | """Some mixin classes for tests"""
13 | from cas_server.default_settings import settings
14 | from django.utils import timezone
15 |
16 | import re
17 | from lxml import etree
18 | from datetime import timedelta
19 |
20 | from cas_server import models
21 | from cas_server.tests.utils import get_auth_client
22 |
23 |
24 | class BaseServicePattern(object):
25 | """Mixing for setting up service pattern for testing"""
26 | @classmethod
27 | def setup_service_patterns(cls, proxy=False):
28 | """setting up service pattern"""
29 | # For general purpose testing
30 | cls.service = "https://www.example.com"
31 | cls.service_pattern = models.ServicePattern.objects.create(
32 | name="example",
33 | pattern=r"^https://www\.example\.com(/.*)?$",
34 | proxy=proxy,
35 | )
36 | models.ReplaceAttributName.objects.create(name="*", service_pattern=cls.service_pattern)
37 |
38 | # For testing the restrict_users attributes
39 | cls.service_restrict_user_fail = "https://restrict_user_fail.example.com"
40 | cls.service_pattern_restrict_user_fail = models.ServicePattern.objects.create(
41 | name="restrict_user_fail",
42 | pattern=r"^https://restrict_user_fail\.example\.com(/.*)?$",
43 | restrict_users=True,
44 | proxy=proxy,
45 | )
46 | cls.service_restrict_user_success = "https://restrict_user_success.example.com"
47 | cls.service_pattern_restrict_user_success = models.ServicePattern.objects.create(
48 | name="restrict_user_success",
49 | pattern=r"^https://restrict_user_success\.example\.com(/.*)?$",
50 | restrict_users=True,
51 | proxy=proxy,
52 | )
53 | models.Username.objects.create(
54 | value=settings.CAS_TEST_USER,
55 | service_pattern=cls.service_pattern_restrict_user_success
56 | )
57 |
58 | # For testing the user attributes filtering conditions
59 | cls.service_filter_fail = "https://filter_fail.example.com"
60 | cls.service_pattern_filter_fail = models.ServicePattern.objects.create(
61 | name="filter_fail",
62 | pattern=r"^https://filter_fail\.example\.com(/.*)?$",
63 | proxy=proxy,
64 | )
65 | models.FilterAttributValue.objects.create(
66 | attribut="right",
67 | pattern="^admin$",
68 | service_pattern=cls.service_pattern_filter_fail
69 | )
70 | cls.service_filter_fail_alt = "https://filter_fail_alt.example.com"
71 | cls.service_pattern_filter_fail_alt = models.ServicePattern.objects.create(
72 | name="filter_fail_alt",
73 | pattern=r"^https://filter_fail_alt\.example\.com(/.*)?$",
74 | proxy=proxy,
75 | )
76 | models.FilterAttributValue.objects.create(
77 | attribut="nom",
78 | pattern="^toto$",
79 | service_pattern=cls.service_pattern_filter_fail_alt
80 | )
81 | cls.service_filter_success = "https://filter_success.example.com"
82 | cls.service_pattern_filter_success = models.ServicePattern.objects.create(
83 | name="filter_success",
84 | pattern=r"^https://filter_success\.example\.com(/.*)?$",
85 | proxy=proxy,
86 | )
87 | models.FilterAttributValue.objects.create(
88 | attribut="email",
89 | pattern="^%s$" % re.escape(settings.CAS_TEST_ATTRIBUTES['email']),
90 | service_pattern=cls.service_pattern_filter_success
91 | )
92 |
93 | # For testing the user_field attributes
94 | cls.service_field_needed_fail = "https://field_needed_fail.example.com"
95 | cls.service_pattern_field_needed_fail = models.ServicePattern.objects.create(
96 | name="field_needed_fail",
97 | pattern=r"^https://field_needed_fail\.example\.com(/.*)?$",
98 | user_field="uid",
99 | proxy=proxy,
100 | )
101 | cls.service_field_needed_success = "https://field_needed_success.example.com"
102 | cls.service_pattern_field_needed_success = models.ServicePattern.objects.create(
103 | name="field_needed_success",
104 | pattern=r"^https://field_needed_success\.example\.com(/.*)?$",
105 | user_field="alias",
106 | proxy=proxy,
107 | )
108 | cls.service_field_needed_success_alt = "https://field_needed_success_alt.example.com"
109 | cls.service_pattern_field_needed_success = models.ServicePattern.objects.create(
110 | name="field_needed_success_alt",
111 | pattern=r"^https://field_needed_success_alt\.example\.com(/.*)?$",
112 | user_field="nom",
113 | proxy=proxy,
114 | )
115 |
116 |
117 | class XmlContent(object):
118 | """Mixin for test on CAS XML responses"""
119 | def assert_error(self, response, code, text=None):
120 | """Assert a validation error"""
121 | self.assertEqual(response.status_code, 200)
122 | root = etree.fromstring(response.content)
123 | error = root.xpath(
124 | "//cas:authenticationFailure",
125 | namespaces={'cas': "http://www.yale.edu/tp/cas"}
126 | )
127 | self.assertEqual(len(error), 1)
128 | self.assertEqual(error[0].attrib['code'], code)
129 | if text is not None:
130 | self.assertEqual(error[0].text, text)
131 |
132 | def assert_success(self, response, username, original_attributes):
133 | """assert a ticket validation success"""
134 | self.assertEqual(response.status_code, 200)
135 |
136 | root = etree.fromstring(response.content)
137 | sucess = root.xpath(
138 | "//cas:authenticationSuccess",
139 | namespaces={'cas': "http://www.yale.edu/tp/cas"}
140 | )
141 | self.assertTrue(sucess)
142 |
143 | users = root.xpath("//cas:user", namespaces={'cas': "http://www.yale.edu/tp/cas"})
144 | self.assertEqual(len(users), 1)
145 | self.assertEqual(users[0].text, username)
146 |
147 | attributes = root.xpath(
148 | "//cas:attributes",
149 | namespaces={'cas': "http://www.yale.edu/tp/cas"}
150 | )
151 | self.assertEqual(len(attributes), 1)
152 | ignore_attrs = {
153 | "authenticationDate", "longTermAuthenticationRequestTokenUsed", "isFromNewLogin"
154 | }
155 | ignored_attrs = 0
156 | attrs1 = set()
157 | for attr in attributes[0]:
158 | name = attr.tag[len("http://www.yale.edu/tp/cas")+2:]
159 | if name not in ignore_attrs:
160 | attrs1.add((name, attr.text))
161 | else:
162 | ignored_attrs += 1
163 |
164 | attributes = root.xpath("//cas:attribute", namespaces={'cas': "http://www.yale.edu/tp/cas"})
165 | self.assertEqual(len(attributes), len(attrs1) + ignored_attrs)
166 | attrs2 = set()
167 | for attr in attributes:
168 | name = attr.attrib['name']
169 | if name not in ignore_attrs:
170 | attrs2.add((name, attr.attrib['value']))
171 | original = set()
172 | for key, value in original_attributes.items():
173 | if isinstance(value, list):
174 | for sub_value in value:
175 | original.add((key, sub_value))
176 | else:
177 | original.add((key, value))
178 | self.assertEqual(attrs1, attrs2)
179 | self.assertEqual(attrs1, original)
180 |
181 | return root
182 |
183 |
184 | class UserModels(object):
185 | """Mixin for test on CAS user models"""
186 | @staticmethod
187 | def expire_user():
188 | """return an expired user"""
189 | client = get_auth_client()
190 |
191 | new_date = timezone.now() - timedelta(seconds=(settings.SESSION_COOKIE_AGE + 600))
192 | models.User.objects.filter(
193 | username=settings.CAS_TEST_USER,
194 | session_key=client.session.session_key
195 | ).update(date=new_date)
196 | return client
197 |
198 | @staticmethod
199 | def tgt_expired_user(sec):
200 | """return a user logged since sec seconds"""
201 | client = get_auth_client()
202 | new_date = timezone.now() - timedelta(seconds=(sec))
203 | models.User.objects.filter(
204 | username=settings.CAS_TEST_USER,
205 | session_key=client.session.session_key
206 | ).update(last_login=new_date)
207 | return client
208 |
209 | @staticmethod
210 | def get_user(client):
211 | """return the user associated with an authenticated client"""
212 | return models.User.objects.get(
213 | username=settings.CAS_TEST_USER,
214 | session_key=client.session.session_key
215 | )
216 |
217 |
218 | class CanLogin(object):
219 | """Assertion about login"""
220 | def assert_logged(
221 | self, client, response, warn=False,
222 | code=200, username=settings.CAS_TEST_USER
223 | ):
224 | """Assertions testing that client is well authenticated"""
225 | self.assertEqual(response.status_code, code)
226 | # this message is displayed to the user upon successful authentication
227 | self.assertIn(
228 | (
229 | b"You have successfully logged into "
230 | b"the Central Authentication Service"
231 | ),
232 | response.content
233 | )
234 | # these session variables a set if usccessfully authenticated
235 | self.assertEqual(client.session["username"], username)
236 | self.assertIs(client.session["warn"], warn)
237 | self.assertIs(client.session["authenticated"], True)
238 |
239 | # on successfull authentication, a corresponding user object is created
240 | self.assertTrue(
241 | models.User.objects.get(
242 | username=username,
243 | session_key=client.session.session_key
244 | )
245 | )
246 |
247 | def assert_login_failed(self, client, response, code=200):
248 | """Assertions testing a failed login attempt"""
249 | self.assertEqual(response.status_code, code)
250 | # this message is displayed to the user upon successful authentication, so it should not
251 | # appear
252 | self.assertNotIn(
253 | (
254 | b"You have successfully logged into "
255 | b"the Central Authentication Service"
256 | ),
257 | response.content
258 | )
259 |
260 | # if authentication has failed, these session variables should not be set
261 | self.assertTrue(client.session.get("username") is None)
262 | self.assertTrue(client.session.get("warn") is None)
263 | self.assertTrue(client.session.get("authenticated") is None)
264 |
265 |
266 | class FederatedIendityProviderModel(object):
267 | """Mixin for test classes using the FederatedIendityProvider model"""
268 | @staticmethod
269 | def setup_federated_identity_provider(providers):
270 | """setting up federated identity providers"""
271 | for suffix, (server_url, cas_protocol_version, verbose_name) in providers.items():
272 | models.FederatedIendityProvider.objects.create(
273 | suffix=suffix,
274 | server_url=server_url,
275 | cas_protocol_version=cas_protocol_version,
276 | verbose_name=verbose_name
277 | )
278 |
--------------------------------------------------------------------------------
/cas_server/tests/test_utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This program is distributed in the hope that it will be useful, but WITHOUT
3 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
5 | # more details.
6 | #
7 | # You should have received a copy of the GNU General Public License version 3
8 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
9 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
10 | #
11 | # (c) 2016-2025 Valentin Samir
12 | """Tests module for utils"""
13 | import django
14 | from django.test import TestCase, RequestFactory
15 | from django.db import connection
16 |
17 | import warnings
18 | import datetime
19 |
20 | from cas_server import utils
21 |
22 |
23 | class CheckPasswordCase(TestCase):
24 | """Tests for the utils function `utils.check_password`"""
25 |
26 | def setUp(self):
27 | """Generate random bytes string that will be used ass passwords"""
28 | self.password1 = utils.gen_saml_id()
29 | self.password2 = utils.gen_saml_id()
30 | if not isinstance(self.password1, bytes): # pragma: no cover executed only in python3
31 | self.password1 = self.password1.encode("utf8")
32 | self.password2 = self.password2.encode("utf8")
33 |
34 | def test_setup(self):
35 | """check that generated password are bytes"""
36 | self.assertIsInstance(self.password1, bytes)
37 | self.assertIsInstance(self.password2, bytes)
38 |
39 | def test_plain(self):
40 | """test the plain auth method"""
41 | self.assertTrue(utils.check_password("plain", self.password1, self.password1, "utf8"))
42 | self.assertFalse(utils.check_password("plain", self.password1, self.password2, "utf8"))
43 |
44 | def test_plain_unicode(self):
45 | """test the plain auth method with unicode input"""
46 | self.assertTrue(
47 | utils.check_password(
48 | "plain",
49 | self.password1.decode("utf8"),
50 | self.password1.decode("utf8"),
51 | "utf8"
52 | )
53 | )
54 | self.assertFalse(
55 | utils.check_password(
56 | "plain",
57 | self.password1.decode("utf8"),
58 | self.password2.decode("utf8"),
59 | "utf8"
60 | )
61 | )
62 |
63 | def test_crypt(self):
64 | """test the crypt auth method"""
65 | # Only run test if crypt is available
66 | if utils.crypt is None:
67 | return
68 | salts = ["$6$UVVAQvrMyXMF3FF3", "aa"]
69 | hashed_password1 = []
70 | for salt in salts:
71 | hashed_password1.append(
72 | utils.crypt.crypt(
73 | self.password1.decode("utf8"),
74 | salt
75 | ).encode("utf8")
76 | )
77 |
78 | for hp1 in hashed_password1:
79 | self.assertTrue(utils.check_password("crypt", self.password1, hp1, "utf8"))
80 | self.assertFalse(utils.check_password("crypt", self.password2, hp1, "utf8"))
81 |
82 | with self.assertRaises(ValueError):
83 | utils.check_password("crypt", self.password1, b"$truc$s$dsdsd", "utf8")
84 |
85 | def test_ldap_password_valid(self):
86 | """test the ldap auth method with all the schemes"""
87 | salt = b"UVVAQvrMyXMF3FF3"
88 | schemes_salt = [b"{SMD5}", b"{SSHA}", b"{SSHA256}", b"{SSHA384}", b"{SSHA512}"]
89 | schemes_nosalt = [b"{MD5}", b"{SHA}", b"{SHA256}", b"{SHA384}", b"{SHA512}"]
90 | hashed_password1 = []
91 | for scheme in schemes_salt:
92 | hashed_password1.append(
93 | utils.LdapHashUserPassword.hash(scheme, self.password1, salt, charset="utf8")
94 | )
95 | for scheme in schemes_nosalt:
96 | hashed_password1.append(
97 | utils.LdapHashUserPassword.hash(scheme, self.password1, charset="utf8")
98 | )
99 | if utils.crypt is not None:
100 | hashed_password1.append(
101 | utils.LdapHashUserPassword.hash(
102 | b"{CRYPT}",
103 | self.password1,
104 | b"$6$UVVAQvrMyXMF3FF3",
105 | charset="utf8"
106 | )
107 | )
108 | for hp1 in hashed_password1:
109 | self.assertIsInstance(hp1, bytes)
110 | self.assertTrue(utils.check_password("ldap", self.password1, hp1, "utf8"))
111 | self.assertFalse(utils.check_password("ldap", self.password2, hp1, "utf8"))
112 |
113 | def test_ldap_password_fail(self):
114 | """test the ldap auth method with malformed hash or bad schemes"""
115 | salt = b"UVVAQvrMyXMF3FF3"
116 | schemes_salt = [b"{SMD5}", b"{SSHA}", b"{SSHA256}", b"{SSHA384}", b"{SSHA512}"]
117 | schemes_nosalt = [b"{MD5}", b"{SHA}", b"{SHA256}", b"{SHA384}", b"{SHA512}"]
118 |
119 | # first try to hash with bad parameters
120 | with self.assertRaises(utils.LdapHashUserPassword.BadScheme):
121 | utils.LdapHashUserPassword.hash(b"TOTO", self.password1)
122 | for scheme in schemes_nosalt:
123 | with self.assertRaises(utils.LdapHashUserPassword.BadScheme):
124 | utils.LdapHashUserPassword.hash(scheme, self.password1, salt)
125 | for scheme in schemes_salt:
126 | with self.assertRaises(utils.LdapHashUserPassword.BadScheme):
127 | utils.LdapHashUserPassword.hash(scheme, self.password1)
128 | if utils.crypt is not None:
129 | with self.assertRaises(utils.LdapHashUserPassword.BadSalt):
130 | utils.LdapHashUserPassword.hash(b'{CRYPT}', self.password1, b"$truc$toto")
131 |
132 | # then try to check hash with bad hashes
133 | with self.assertRaises(utils.LdapHashUserPassword.BadHash):
134 | utils.check_password("ldap", self.password1, b"TOTOssdsdsd", "utf8")
135 | for scheme in schemes_salt:
136 | # bad length
137 | with self.assertRaises(utils.LdapHashUserPassword.BadHash):
138 | utils.check_password("ldap", self.password1, scheme + b"dG90b3E8ZHNkcw==", "utf8")
139 | # bad base64
140 | with self.assertRaises(utils.LdapHashUserPassword.BadHash):
141 | utils.check_password("ldap", self.password1, scheme + b"dG90b3E8ZHNkcw", "utf8")
142 |
143 | def test_hex(self):
144 | """test all the hex_HASH method: the hashed password is a simple hash of the password"""
145 | hashes = ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"]
146 | hashed_password1 = []
147 | for hash_scheme in hashes:
148 | hashed_password1.append(
149 | (
150 | "hex_%s" % hash_scheme,
151 | getattr(utils.hashlib, hash_scheme)(self.password1).hexdigest()
152 | )
153 | )
154 | for (method, hp1) in hashed_password1:
155 | self.assertTrue(utils.check_password(method, self.password1, hp1, "utf8"))
156 | self.assertFalse(utils.check_password(method, self.password2, hp1, "utf8"))
157 |
158 | def test_bad_method(self):
159 | """try to check password with a bad method, should raise a ValueError"""
160 | with self.assertRaises(ValueError):
161 | utils.check_password("test", self.password1, b"$truc$s$dsdsd", "utf8")
162 |
163 |
164 | class UtilsTestCase(TestCase):
165 | """tests for some little utils functions"""
166 | def test_import_attr(self):
167 | """
168 | test the import_attr function. Feeded with a dotted path string, it should
169 | import the dotted module and return that last componend of the dotted path
170 | (function, class or variable)
171 | """
172 | with self.assertRaises(ImportError):
173 | utils.import_attr('toto.titi.tutu')
174 | with self.assertRaises(AttributeError):
175 | utils.import_attr('cas_server.utils.toto')
176 | with self.assertRaises(ValueError):
177 | utils.import_attr('toto')
178 | if django.VERSION < (3, 2):
179 | self.assertEqual(
180 | utils.import_attr('cas_server.default_app_config'),
181 | 'cas_server.apps.CasAppConfig'
182 | )
183 | self.assertEqual(utils.import_attr(utils), utils)
184 |
185 | def test_update_url(self):
186 | """
187 | test the update_url function. Given an url with possible GET parameter and a dict
188 | the function build a url with GET parameters updated by the dictionnary
189 | """
190 | url1 = utils.update_url(u"https://www.example.com?toto=1", {u"tata": u"2"})
191 | url2 = utils.update_url(b"https://www.example.com?toto=1", {b"tata": b"2"})
192 | self.assertEqual(url1, u"https://www.example.com?tata=2&toto=1")
193 | self.assertEqual(url2, u"https://www.example.com?tata=2&toto=1")
194 |
195 | url3 = utils.update_url(u"https://www.example.com?toto=1", {u"toto": u"2"})
196 | self.assertEqual(url3, u"https://www.example.com?toto=2")
197 |
198 | def test_crypt_salt_is_valid(self):
199 | """test the function crypt_salt_is_valid who test if a crypt salt is valid"""
200 | self.assertFalse(utils.crypt_salt_is_valid("")) # len 0
201 | self.assertFalse(utils.crypt_salt_is_valid("a")) # len 1
202 | self.assertFalse(utils.crypt_salt_is_valid("$$")) # start with $ followed by $
203 | self.assertFalse(utils.crypt_salt_is_valid("$toto")) # start with $ but no secondary $
204 | self.assertFalse(utils.crypt_salt_is_valid("$toto$toto")) # algorithm toto not known
205 |
206 | def test_get_current_url(self):
207 | """test the function get_current_url"""
208 | factory = RequestFactory()
209 | request = factory.get('/truc/muche?test=1')
210 | self.assertEqual(utils.get_current_url(request), 'http://testserver/truc/muche?test=1')
211 | self.assertEqual(
212 | utils.get_current_url(request, ignore_params={'test'}),
213 | 'http://testserver/truc/muche'
214 | )
215 |
216 | def test_get_tuple(self):
217 | """test the function get_tuple"""
218 | test_tuple = (1, 2, 3)
219 | for index, value in enumerate(test_tuple):
220 | self.assertEqual(utils.get_tuple(test_tuple, index), value)
221 | self.assertEqual(utils.get_tuple(test_tuple, 3), None)
222 | self.assertEqual(utils.get_tuple(test_tuple, 3, 'toto'), 'toto')
223 | self.assertEqual(utils.get_tuple(None, 3), None)
224 |
225 | def test_last_version(self):
226 | """
227 | test the function last_version. An internet connection is needed, if you do not have
228 | one, this test will fail and you should ignore it.
229 | """
230 | try:
231 | # first check if pypi is available
232 | utils.requests.get("https://pypi.org/simple/django-cas-server/")
233 | except utils.requests.exceptions.RequestException:
234 | warnings.warn(
235 | (
236 | "Pypi seems not available, perhaps you do not have internet access. "
237 | "Consequently, the test cas_server.tests.test_utils.UtilsTestCase.test_last_"
238 | "version is ignored"
239 | ),
240 | RuntimeWarning
241 | )
242 | else:
243 | version = utils.last_version()
244 | self.assertIsInstance(version, str)
245 | self.assertEqual(len(version.split('.')), 3)
246 |
247 | # version is cached 24h so calling it a second time should return the save value
248 | self.assertEqual(version, utils.last_version())
249 |
250 | def test_dictfetchall(self):
251 | """test the function dictfetchall"""
252 | with connection.cursor() as curs:
253 | curs.execute("SELECT * FROM django_migrations")
254 | results = utils.dictfetchall(curs)
255 | self.assertIsInstance(results, list)
256 | self.assertTrue(len(results) > 0)
257 | for result in results:
258 | self.assertIsInstance(result, dict)
259 | self.assertIn('applied', result)
260 | self.assertIsInstance(result['applied'], datetime.datetime)
261 |
262 | def test_regexpr_validator(self):
263 | """test the function regexpr_validator"""
264 | utils.regexpr_validator("^a$")
265 | with self.assertRaises(utils.ValidationError):
266 | utils.regexpr_validator("[")
267 |
--------------------------------------------------------------------------------
/cas_server/locale/pt_BR/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | msgid ""
7 | msgstr ""
8 | "Project-Id-Version: \n"
9 | "Report-Msgid-Bugs-To: \n"
10 | "POT-Creation-Date: 2017-08-22 08:18-0300\n"
11 | "PO-Revision-Date: 2017-08-29 18:09+0200\n"
12 | "Language-Team: Roberto Morati \n"
13 | "Language: pt_BR\n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=2; plural=(n > 1);\n"
18 | "Last-Translator: Valentin Samir \n"
19 | "X-Generator: Poedit 1.8.11\n"
20 |
21 | #: cas_server/apps.py:25 cas_server/templates/cas_server/base.html:7
22 | #: cas_server/templates/cas_server/base.html:26
23 | msgid "Central Authentication Service"
24 | msgstr "Central de Autenticação de Serviços"
25 |
26 | #: cas_server/default_settings.py:201
27 | msgid ""
28 | "The Central Authentication Service grants you access to most of our websites by "
29 | "authenticating only once, so you don't need to type your credentials again unless your "
30 | "session expires or you logout."
31 | msgstr ""
32 | "A Central de Autenticação de Serviços garante seu acesso à maioria dos nossos sitespor "
33 | "meio de uma única autenticação, então você não precisa digitar suas "
34 | "credenciaisnovamente, ao menos que sua sessão expire ou seu logout."
35 |
36 | #: cas_server/forms.py:85
37 | msgid "Identity provider"
38 | msgstr "Provedor de identidade"
39 |
40 | #: cas_server/forms.py:89 cas_server/forms.py:111
41 | msgid "Warn me before logging me into other sites."
42 | msgstr "Avise-me antes de me registrar em outros sites"
43 |
44 | #: cas_server/forms.py:93
45 | msgid "Remember the identity provider"
46 | msgstr "Relembrar o provedor de identidade"
47 |
48 | #: cas_server/forms.py:104 cas_server/models.py:638
49 | msgid "username"
50 | msgstr "usuário"
51 |
52 | #: cas_server/forms.py:108
53 | msgid "password"
54 | msgstr "senha"
55 |
56 | #: cas_server/forms.py:131
57 | msgid "The credentials you provided cannot be determined to be authentic."
58 | msgstr "As credenciais que você forneceu não podem ser determinadas como autênticas."
59 |
60 | #: cas_server/forms.py:183
61 | msgid "User not found in the temporary database, please try to reconnect"
62 | msgstr "Usuário não encontrado na base de dados temporária, por favor, tente se reconectar"
63 |
64 | #: cas_server/forms.py:197
65 | msgid "service"
66 | msgstr ""
67 |
68 | #: cas_server/management/commands/cas_clean_federate.py:20
69 | msgid "Clean old federated users"
70 | msgstr ""
71 |
72 | #: cas_server/management/commands/cas_clean_sessions.py:22
73 | msgid "Clean deleted sessions"
74 | msgstr ""
75 |
76 | #: cas_server/management/commands/cas_clean_tickets.py:22
77 | msgid "Clean old tickets"
78 | msgstr ""
79 |
80 | #: cas_server/models.py:71
81 | msgid "identity provider"
82 | msgstr "provedor de identidade"
83 |
84 | #: cas_server/models.py:72
85 | msgid "identity providers"
86 | msgstr "provedores de identidade"
87 |
88 | #: cas_server/models.py:78
89 | msgid "suffix"
90 | msgstr ""
91 |
92 | #: cas_server/models.py:80
93 | msgid "Suffix append to backend CAS returned username: ``returned_username`` @ ``suffix``."
94 | msgstr ""
95 |
96 | #: cas_server/models.py:87
97 | msgid "server url"
98 | msgstr ""
99 |
100 | #: cas_server/models.py:97
101 | msgid "CAS protocol version"
102 | msgstr ""
103 |
104 | #: cas_server/models.py:99
105 | msgid "Version of the CAS protocol to use when sending requests the the backend CAS."
106 | msgstr ""
107 |
108 | #: cas_server/models.py:106
109 | msgid "verbose name"
110 | msgstr ""
111 |
112 | #: cas_server/models.py:107
113 | msgid "Name for this identity provider displayed on the login page."
114 | msgstr "Nome para exibir o provedor de identidade na página de login."
115 |
116 | #: cas_server/models.py:113 cas_server/models.py:490
117 | msgid "position"
118 | msgstr ""
119 |
120 | #: cas_server/models.py:127
121 | msgid "display"
122 | msgstr ""
123 |
124 | #: cas_server/models.py:128
125 | msgid "Display the provider on the login page."
126 | msgstr ""
127 |
128 | #: cas_server/models.py:166
129 | msgid "Federated user"
130 | msgstr ""
131 |
132 | #: cas_server/models.py:167
133 | msgid "Federated users"
134 | msgstr ""
135 |
136 | #: cas_server/models.py:246
137 | msgid "User attributes cache"
138 | msgstr ""
139 |
140 | #: cas_server/models.py:247
141 | msgid "User attributes caches"
142 | msgstr ""
143 |
144 | #: cas_server/models.py:271
145 | msgid "User"
146 | msgstr ""
147 |
148 | #: cas_server/models.py:272
149 | msgid "Users"
150 | msgstr ""
151 |
152 | #: cas_server/models.py:364
153 | #, python-format
154 | msgid "Error during service logout %s"
155 | msgstr ""
156 |
157 | #: cas_server/models.py:484
158 | msgid "Service pattern"
159 | msgstr ""
160 |
161 | #: cas_server/models.py:485
162 | msgid "Services patterns"
163 | msgstr ""
164 |
165 | #: cas_server/models.py:491
166 | msgid "service patterns are sorted using the position attribute"
167 | msgstr ""
168 |
169 | #: cas_server/models.py:499 cas_server/models.py:664
170 | msgid "name"
171 | msgstr ""
172 |
173 | #: cas_server/models.py:500
174 | msgid "A name for the service"
175 | msgstr ""
176 |
177 | #: cas_server/models.py:508 cas_server/models.py:707 cas_server/models.py:737
178 | msgid "pattern"
179 | msgstr ""
180 |
181 | #: cas_server/models.py:510
182 | msgid ""
183 | "A regular expression matching services. Will usually looks like '^https://some\\.server"
184 | "\\.com/path/.*$'.As it is a regular expression, special character must be escaped with a "
185 | "'\\'."
186 | msgstr ""
187 |
188 | #: cas_server/models.py:521
189 | msgid "user field"
190 | msgstr ""
191 |
192 | #: cas_server/models.py:522
193 | msgid "Name of the attribute to transmit as username, empty = login"
194 | msgstr ""
195 |
196 | #: cas_server/models.py:527
197 | msgid "restrict username"
198 | msgstr ""
199 |
200 | #: cas_server/models.py:528
201 | msgid "Limit username allowed to connect to the list provided bellow"
202 | msgstr ""
203 |
204 | #: cas_server/models.py:533
205 | msgid "proxy"
206 | msgstr ""
207 |
208 | #: cas_server/models.py:534
209 | msgid "Proxy tickets can be delivered to the service"
210 | msgstr ""
211 |
212 | #: cas_server/models.py:540
213 | msgid "proxy callback"
214 | msgstr ""
215 |
216 | #: cas_server/models.py:541
217 | msgid "can be used as a proxy callback to deliver PGT"
218 | msgstr ""
219 |
220 | #: cas_server/models.py:548
221 | msgid "single log out"
222 | msgstr ""
223 |
224 | #: cas_server/models.py:549
225 | msgid "Enable SLO for the service"
226 | msgstr ""
227 |
228 | #: cas_server/models.py:558
229 | msgid ""
230 | "URL where the SLO request will be POST. empty = service url\n"
231 | "This is usefull for non HTTP proxied services."
232 | msgstr ""
233 |
234 | #: cas_server/models.py:639
235 | msgid "username allowed to connect to the service"
236 | msgstr ""
237 |
238 | #: cas_server/models.py:665
239 | msgid "name of an attribute to send to the service, use * for all attributes"
240 | msgstr ""
241 |
242 | #: cas_server/models.py:672 cas_server/models.py:745
243 | msgid "replace"
244 | msgstr ""
245 |
246 | #: cas_server/models.py:673
247 | msgid ""
248 | "name under which the attribute will be show to the service. empty = default name of the "
249 | "attribut"
250 | msgstr ""
251 |
252 | #: cas_server/models.py:700 cas_server/models.py:731
253 | msgid "attribute"
254 | msgstr ""
255 |
256 | #: cas_server/models.py:701
257 | msgid "Name of the attribute which must verify pattern"
258 | msgstr ""
259 |
260 | #: cas_server/models.py:708
261 | msgid "a regular expression"
262 | msgstr ""
263 |
264 | #: cas_server/models.py:732
265 | msgid "Name of the attribute for which the value must be replace"
266 | msgstr ""
267 |
268 | #: cas_server/models.py:738
269 | msgid "An regular expression maching whats need to be replaced"
270 | msgstr ""
271 |
272 | #: cas_server/models.py:746
273 | msgid "replace expression, groups are capture by \\1, \\2 …"
274 | msgstr ""
275 |
276 | #: cas_server/templates/cas_server/base.html:43
277 | #, python-format
278 | msgid ""
279 | "A new version of the application is available. This instance runs %(VERSION)s and the "
280 | "last version is %(LAST_VERSION)s. Please consider upgrading."
281 | msgstr ""
282 | "Uma nova versão da aplicação está disponível. Está instância usa a versão %(VERSION)s e "
283 | "a última versão é %(LAST_VERSION)s. Por favor, considere a atualização."
284 |
285 | #: cas_server/templates/cas_server/logged.html:4
286 | msgid ""
287 | "Log In Successful You have successfully logged into the Central Authentication "
288 | "Service. For security reasons, please Log Out and Exit your web browser when you are "
289 | "done accessing services that require authentication!"
290 | msgstr ""
291 | "Log In realizado com sucesso Você foi conectado com sucesso a Central de "
292 | "Autenticação de Serviços. Por razões de segurança, faça o Log Out e saia do seu "
293 | "navegador quando você terminar de acessar os serviços que exigem auntenticação!"
294 |
295 | #: cas_server/templates/cas_server/logged.html:8
296 | msgid "Log me out from all my sessions"
297 | msgstr "Desconecte-me de todas as sessões"
298 |
299 | #: cas_server/templates/cas_server/logged.html:14
300 | msgid "Forget the identity provider"
301 | msgstr "Esquecer o provedor de identidade"
302 |
303 | #: cas_server/templates/cas_server/logged.html:18
304 | msgid "Logout"
305 | msgstr ""
306 |
307 | #: cas_server/templates/cas_server/login.html:6
308 | msgid "Please log in"
309 | msgstr "Por favor, faça log in"
310 |
311 | #: cas_server/templates/cas_server/login.html:14
312 | msgid "Login"
313 | msgstr ""
314 |
315 | #: cas_server/templates/cas_server/warn.html:9
316 | msgid "Connect to the service"
317 | msgstr ""
318 |
319 | #: cas_server/utils.py:744
320 | #, python-format
321 | msgid "\"%(value)s\" is not a valid regular expression"
322 | msgstr ""
323 |
324 | #: cas_server/views.py:185
325 | msgid ""
326 | "Logout successful You have successfully logged out from the Central "
327 | "Authentication Service. For security reasons, close your web browser."
328 | msgstr ""
329 | "Logout realizado com sucesso Você foi desconectado com sucesso da Central de "
330 | "Autenticação de Serviços. Por razões de segurança, feche seu navegador."
331 |
332 | #: cas_server/views.py:191
333 | #, python-format
334 | msgid ""
335 | "Logout successful You have successfully logged out from %s sessions of the "
336 | "Central Authentication Service. For security reasons, close your web browser."
337 | msgstr ""
338 | "Logout realizado com sucesso Você foi desconectado com sucesso da %s sessão da "
339 | "Centralde Autenticação de Serviços. Por razões de segurança, feche seu navegador."
340 |
341 | #: cas_server/views.py:198
342 | msgid ""
343 | "Logout successful You were already logged out from the Central Authentication "
344 | "Service. For security reasons, close your web browser."
345 | msgstr ""
346 | "Logout realizado com sucesso Você já está desconectado da Central de "
347 | "Autenticação de Serviços. Por razões de segurança, feche seu navegador."
348 |
349 | #: cas_server/views.py:378
350 | #, python-format
351 | msgid ""
352 | "Invalid response from your identity provider CAS upon ticket %(ticket)s validation: "
353 | "%(error)r"
354 | msgstr ""
355 | "Resposta inválida do provedor de identidade CAS sobre o ticket %(ticket)svalidação: "
356 | "%(error)r"
357 |
358 | #: cas_server/views.py:500
359 | msgid "Invalid login ticket, please try to log in again"
360 | msgstr "Ticket de login inválido, por favor tente novamente"
361 |
362 | #: cas_server/views.py:693
363 | #, python-format
364 | msgid "Authentication has been required by service %(name)s (%(url)s)"
365 | msgstr "Autenticação requerida pelo serviço %(name)s (%(url)s)"
366 |
367 | #: cas_server/views.py:731
368 | #, python-format
369 | msgid "Service %(url)s not allowed."
370 | msgstr "Serviço %(url)s não permitido"
371 |
372 | #: cas_server/views.py:738
373 | msgid "Username not allowed"
374 | msgstr "Usuário não permitido"
375 |
376 | #: cas_server/views.py:745
377 | msgid "User characteristics not allowed"
378 | msgstr "Características de usuário não permitida"
379 |
380 | #: cas_server/views.py:752
381 | #, python-format
382 | msgid "The attribute %(field)s is needed to use that service"
383 | msgstr "O atributo %(field)s é necessário para usar o serviço"
384 |
385 | #: cas_server/views.py:842
386 | #, python-format
387 | msgid "Authentication renewal required by service %(name)s (%(url)s)."
388 | msgstr "Renovação da autenticação requerida pelo serviço %(name)s (%(url)s)."
389 |
390 | #: cas_server/views.py:849
391 | #, python-format
392 | msgid "Authentication required by service %(name)s (%(url)s)."
393 | msgstr "Autenticação requerida pelo serviço %(name)s (%(url)s)."
394 |
395 | #: cas_server/views.py:856
396 | #, python-format
397 | msgid "Service %s not allowed"
398 | msgstr "Serviço %s não permitido"
399 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # django-cas-server documentation build configuration file, created by
4 | # sphinx-quickstart on Tue Jul 5 12:11:50 2016.
5 | #
6 | # This file is execfile()d with the current directory set to its
7 | # containing dir.
8 | #
9 | # Note that not all possible configuration values are present in this
10 | # autogenerated file.
11 | #
12 | # All configuration values have a default; values that are commented out
13 | # serve to show the default.
14 |
15 | # If extensions (or modules to document with autodoc) are in another directory,
16 | # add these directories to sys.path here. If the directory is relative to the
17 | # documentation root, use os.path.abspath to make it absolute, like shown here.
18 | #
19 | import os
20 | import sys
21 | sys.path.insert(0, os.path.abspath('.'))
22 | sys.path.append(os.path.abspath('..'))
23 | sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "_ext")))
24 |
25 | import setup as mysetup
26 |
27 | os.environ['DJANGO_SETTINGS_MODULE'] = 'cas_server.tests.settings'
28 |
29 | import django
30 | django.setup()
31 | # -- General configuration ------------------------------------------------
32 |
33 | # If your documentation needs a minimal Sphinx version, state it here.
34 | #
35 | # needs_sphinx = '1.0'
36 |
37 | # Add any Sphinx extension module names here, as strings. They can be
38 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
39 | # ones.
40 | extensions = [
41 | 'djangodocs',
42 | 'sphinx.ext.autodoc',
43 | 'sphinx.ext.doctest',
44 | 'sphinx.ext.intersphinx',
45 | 'sphinx.ext.coverage',
46 | 'sphinx.ext.viewcode',
47 | ]
48 |
49 | # Add any paths that contain templates here, relative to this directory.
50 | templates_path = ['_templates']
51 |
52 | # The suffix(es) of source filenames.
53 | # You can specify multiple suffix as a list of string:
54 | #
55 | # source_suffix = ['.rst', '.md']
56 | source_suffix = '.rst'
57 |
58 | # The encoding of source files.
59 | #
60 | # source_encoding = 'utf-8-sig'
61 |
62 | # The master toctree document.
63 | master_doc = 'index'
64 |
65 | # General information about the project.
66 | project = u'django-cas-server'
67 | copyright = u'2016-2025, Valentin Samir'
68 | author = u'Valentin Samir'
69 |
70 | # The version info for the project you're documenting, acts as replacement for
71 | # |version| and |release|, also used in various other places throughout the
72 | # built documents.
73 | #
74 | # The short X.Y version.
75 | version = mysetup.VERSION
76 | # The full version, including alpha/beta/rc tags.
77 | release = version
78 |
79 | # The language for content autogenerated by Sphinx. Refer to documentation
80 | # for a list of supported languages.
81 | #
82 | # This is also used if you do content translation via gettext catalogs.
83 | # Usually you set "language" from the command line for these cases.
84 | language = 'en'
85 |
86 | # There are two options for replacing |today|: either, you set today to some
87 | # non-false value, then it is used:
88 | #
89 | # today = ''
90 | #
91 | # Else, today_fmt is used as the format for a strftime call.
92 | #
93 | # today_fmt = '%B %d, %Y'
94 |
95 | # List of patterns, relative to source directory, that match files and
96 | # directories to ignore when looking for source files.
97 | # This patterns also effect to html_static_path and html_extra_path
98 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
99 |
100 | # The reST default role (used for this markup: `text`) to use for all
101 | # documents.
102 | #
103 | # default_role = None
104 |
105 | # If true, '()' will be appended to :func: etc. cross-reference text.
106 | #
107 | # add_function_parentheses = True
108 |
109 | # If true, the current module name will be prepended to all description
110 | # unit titles (such as .. function::).
111 | #
112 | # add_module_names = True
113 |
114 | # If true, sectionauthor and moduleauthor directives will be shown in the
115 | # output. They are ignored by default.
116 | #
117 | # show_authors = False
118 |
119 | # The name of the Pygments (syntax highlighting) style to use.
120 | pygments_style = 'sphinx'
121 |
122 | # A list of ignored prefixes for module index sorting.
123 | # modindex_common_prefix = []
124 |
125 | # If true, keep warnings as "system message" paragraphs in the built documents.
126 | # keep_warnings = False
127 |
128 | # If true, `todo` and `todoList` produce output, else they produce nothing.
129 | todo_include_todos = False
130 |
131 |
132 | # -- Options for HTML output ----------------------------------------------
133 |
134 | # The theme to use for HTML and HTML Help pages. See the documentation for
135 | # a list of builtin themes.
136 | #
137 | #html_theme = 'alabaster'
138 | html_theme = 'sphinx_rtd_theme'
139 |
140 | # Theme options are theme-specific and customize the look and feel of a theme
141 | # further. For a list of options available for each theme, see the
142 | # documentation.
143 | #
144 | # html_theme_options = {}
145 |
146 | # Add any paths that contain custom themes here, relative to this directory.
147 | # html_theme_path = []
148 |
149 | # The name for this set of Sphinx documents.
150 | # " v documentation" by default.
151 | #
152 | # html_title = u'django-cas-server v5.0'
153 |
154 | # A shorter title for the navigation bar. Default is the same as html_title.
155 | #
156 | # html_short_title = None
157 |
158 | # The name of an image file (relative to this directory) to place at the top
159 | # of the sidebar.
160 | #
161 | # html_logo = None
162 |
163 | # The name of an image file (relative to this directory) to use as a favicon of
164 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
165 | # pixels large.
166 | #
167 | # html_favicon = None
168 |
169 | # Add any paths that contain custom static files (such as style sheets) here,
170 | # relative to this directory. They are copied after the builtin static files,
171 | # so a file named "default.css" will overwrite the builtin "default.css".
172 | html_static_path = ['_static']
173 |
174 | # Add any extra paths that contain custom files (such as robots.txt or
175 | # .htaccess) here, relative to this directory. These files are copied
176 | # directly to the root of the documentation.
177 | #
178 | # html_extra_path = []
179 |
180 | # If not None, a 'Last updated on:' timestamp is inserted at every page
181 | # bottom, using the given strftime format.
182 | # The empty string is equivalent to '%b %d, %Y'.
183 | #
184 | # html_last_updated_fmt = None
185 |
186 | # If true, SmartyPants will be used to convert quotes and dashes to
187 | # typographically correct entities.
188 | #
189 | # html_use_smartypants = True
190 |
191 | # Custom sidebar templates, maps document names to template names.
192 | #
193 | # html_sidebars = {}
194 |
195 | # Additional templates that should be rendered to pages, maps page names to
196 | # template names.
197 | #
198 | # html_additional_pages = {}
199 |
200 | # If false, no module index is generated.
201 | #
202 | # html_domain_indices = True
203 |
204 | # If false, no index is generated.
205 | #
206 | # html_use_index = True
207 |
208 | # If true, the index is split into individual pages for each letter.
209 | #
210 | # html_split_index = False
211 |
212 | # If true, links to the reST sources are added to the pages.
213 | #
214 | # html_show_sourcelink = True
215 |
216 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
217 | #
218 | # html_show_sphinx = True
219 |
220 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
221 | #
222 | # html_show_copyright = True
223 |
224 | # If true, an OpenSearch description file will be output, and all pages will
225 | # contain a tag referring to it. The value of this option must be the
226 | # base URL from which the finished HTML is served.
227 | #
228 | # html_use_opensearch = ''
229 |
230 | # This is the file name suffix for HTML files (e.g. ".xhtml").
231 | # html_file_suffix = None
232 |
233 | # Language to be used for generating the HTML full-text search index.
234 | # Sphinx supports the following languages:
235 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
236 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh'
237 | #
238 | # html_search_language = 'en'
239 |
240 | # A dictionary with options for the search language support, empty by default.
241 | # 'ja' uses this config value.
242 | # 'zh' user can custom change `jieba` dictionary path.
243 | #
244 | # html_search_options = {'type': 'default'}
245 |
246 | # The name of a javascript file (relative to the configuration directory) that
247 | # implements a search results scorer. If empty, the default will be used.
248 | #
249 | # html_search_scorer = 'scorer.js'
250 |
251 | # Output file base name for HTML help builder.
252 | htmlhelp_basename = 'django-cas-serverdoc'
253 |
254 | # -- Options for LaTeX output ---------------------------------------------
255 |
256 | latex_elements = {
257 | # The paper size ('letterpaper' or 'a4paper').
258 | #
259 | # 'papersize': 'letterpaper',
260 |
261 | # The font size ('10pt', '11pt' or '12pt').
262 | #
263 | # 'pointsize': '10pt',
264 |
265 | # Additional stuff for the LaTeX preamble.
266 | #
267 | # 'preamble': '',
268 |
269 | # Latex figure (float) alignment
270 | #
271 | # 'figure_align': 'htbp',
272 | }
273 |
274 | # Grouping the document tree into LaTeX files. List of tuples
275 | # (source start file, target name, title,
276 | # author, documentclass [howto, manual, or own class]).
277 | latex_documents = [
278 | (master_doc, 'django-cas-server.tex', u'django-cas-server Documentation',
279 | u'Valentin Samir', 'manual'),
280 | ]
281 |
282 | # The name of an image file (relative to this directory) to place at the top of
283 | # the title page.
284 | #
285 | # latex_logo = None
286 |
287 | # For "manual" documents, if this is true, then toplevel headings are parts,
288 | # not chapters.
289 | #
290 | # latex_use_parts = False
291 |
292 | # If true, show page references after internal links.
293 | #
294 | # latex_show_pagerefs = False
295 |
296 | # If true, show URL addresses after external links.
297 | #
298 | # latex_show_urls = False
299 |
300 | # Documents to append as an appendix to all manuals.
301 | #
302 | # latex_appendices = []
303 |
304 | # If false, no module index is generated.
305 | #
306 | # latex_domain_indices = True
307 |
308 |
309 | # -- Options for manual page output ---------------------------------------
310 |
311 | # One entry per manual page. List of tuples
312 | # (source start file, name, description, authors, manual section).
313 | man_pages = [
314 | (master_doc, 'django-cas-server', u'django-cas-server Documentation',
315 | [author], 1)
316 | ]
317 |
318 | # If true, show URL addresses after external links.
319 | #
320 | # man_show_urls = False
321 |
322 |
323 | # -- Options for Texinfo output -------------------------------------------
324 |
325 | # Grouping the document tree into Texinfo files. List of tuples
326 | # (source start file, target name, title, author,
327 | # dir menu entry, description, category)
328 | texinfo_documents = [
329 | (master_doc, 'django-cas-server', u'django-cas-server Documentation',
330 | author, 'django-cas-server', 'One line description of project.',
331 | 'Miscellaneous'),
332 | ]
333 |
334 | # Documents to append as an appendix to all manuals.
335 | #
336 | # texinfo_appendices = []
337 |
338 | # If false, no module index is generated.
339 | #
340 | # texinfo_domain_indices = True
341 |
342 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
343 | #
344 | # texinfo_show_urls = 'footnote'
345 |
346 | # If true, do not generate a @detailmenu in the "Top" node's menu.
347 | #
348 | # texinfo_no_detailmenu = False
349 |
350 | # Example configuration for intersphinx: refer to the Python standard library.
351 | intersphinx_mapping_download_missing = True
352 |
353 | def check_object_path(key, url, path, fallback=None):
354 | if os.path.isfile(path):
355 | return {key: (url, path)}
356 | if fallback is not None:
357 | path = os.path.abspath(os.path.join(os.path.dirname(__file__), "{}.inv".format(key)))
358 | if not os.path.isfile(path) and intersphinx_mapping_download_missing:
359 | import requests
360 | r = requests.get(fallback)
361 | with open(path + '.new', 'wb') as f:
362 | f.write(r.content)
363 | os.rename(path + '.new', path)
364 | if os.path.isfile(path):
365 | return {key: (url, path)}
366 | return {}
367 |
368 | intersphinx_mapping = {}
369 | intersphinx_mapping.update(check_object_path(
370 | 'python',
371 | 'https://docs.python.org/{v}/'.format(
372 | v='.'.join(map(str, sys.version_info[0:2]))
373 | ),
374 | '/usr/share/doc/python{v}/html/objects.inv'.format(
375 | v='.'.join(map(str, sys.version_info[0:2]))
376 | ),
377 | 'https://docs.python.org/{v}/objects.inv'.format(
378 | v='.'.join(map(str, sys.version_info[0:2]))
379 | )
380 | ))
381 |
382 | intersphinx_mapping.update(check_object_path(
383 | 'django',
384 | 'https://docs.djangoproject.com/en/stable/',
385 | '/usr/share/doc/python-django-doc/html/objects.inv',
386 | "https://docs.djangoproject.com/en/stable/_objects"
387 | ))
388 |
389 | autodoc_member_order = 'bysource'
390 |
391 | locale_dirs = ['../test_venv/lib/python2.7/site-packages/django/conf/locale/']
392 |
393 |
--------------------------------------------------------------------------------
/cas_server/locale/nl/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2017-03-29 12:24+0200\n"
12 | "PO-Revision-Date: 2017-03-29 13:06+0020\n"
13 | "Last-Translator: Joriks Kraaikamp \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=UTF-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
20 | "X-Translated-Using: Mobetta 0.0.2\n"
21 |
22 | #: apps.py:25 templates/cas_server/base.html:7
23 | #: templates/cas_server/base.html:26
24 | msgid "Central Authentication Service"
25 | msgstr "Centrale Authenticatie Service"
26 |
27 | #: default_settings.py:201
28 | msgid ""
29 | "The Central Authentication Service grants you access to most of our websites"
30 | " by authenticating only once, so you don't need to type your credentials "
31 | "again unless your session expires or you logout."
32 | msgstr ""
33 | "De Centrale Authenticatie Service geeft je toegang tot onze sites door 1 "
34 | "keer in te loggen, behalve als je sessie is verlopen of als je hebt "
35 | "uitgelogd."
36 |
37 | #: forms.py:85
38 | msgid "Identity provider"
39 | msgstr "Identiteit provider"
40 |
41 | #: forms.py:89 forms.py:111
42 | msgid "Warn me before logging me into other sites."
43 | msgstr "Waarschuw me voor het inloggen op andere websites."
44 |
45 | #: forms.py:93
46 | msgid "Remember the identity provider"
47 | msgstr "Onthoudt deze identiteit provider"
48 |
49 | #: forms.py:104 models.py:638
50 | msgid "username"
51 | msgstr "gebruikersnaam"
52 |
53 | #: forms.py:108
54 | msgid "password"
55 | msgstr "wachtwoord"
56 |
57 | #: forms.py:131
58 | msgid "The credentials you provided cannot be determined to be authentic."
59 | msgstr "De inloggegevens waren niet correct."
60 |
61 | #: forms.py:183
62 | msgid "User not found in the temporary database, please try to reconnect"
63 | msgstr ""
64 | "De gebruiker is niet gevonden in de tijdelijke database, probeer het opnieuw"
65 |
66 | #: forms.py:197
67 | msgid "service"
68 | msgstr "dienst"
69 |
70 | #: management/commands/cas_clean_federate.py:20
71 | msgid "Clean old federated users"
72 | msgstr "Schoon oude gefedereerde gebruikers op"
73 |
74 | #: management/commands/cas_clean_sessions.py:22
75 | msgid "Clean deleted sessions"
76 | msgstr "Schoon verwijderde sessies op"
77 |
78 | #: management/commands/cas_clean_tickets.py:22
79 | msgid "Clean old tickets"
80 | msgstr "Schoon oude tickets op"
81 |
82 | #: models.py:71
83 | msgid "identity provider"
84 | msgstr "identiteit provider"
85 |
86 | #: models.py:72
87 | msgid "identity providers"
88 | msgstr "Identiteit providers"
89 |
90 | #: models.py:78
91 | msgid "suffix"
92 | msgstr "achtervoegsel"
93 |
94 | #: models.py:80
95 | msgid ""
96 | "Suffix append to backend CAS returned username: ``returned_username`` @ "
97 | "``suffix``."
98 | msgstr ""
99 | "Achtervoegsel toevoegen aan de gebruikersnaam door CAS backend: "
100 | "``teruggegeven_gebruikersnaam`` @ ``achtervoegsel``."
101 |
102 | #: models.py:87
103 | msgid "server url"
104 | msgstr "server url"
105 |
106 | #: models.py:97
107 | msgid "CAS protocol version"
108 | msgstr "CAS protocol versie"
109 |
110 | #: models.py:99
111 | msgid ""
112 | "Version of the CAS protocol to use when sending requests the the backend "
113 | "CAS."
114 | msgstr ""
115 | "Versie van het CAS protocol wanneer we een request sturen van de CAS "
116 | "backend."
117 |
118 | #: models.py:106
119 | msgid "verbose name"
120 | msgstr "uitgebreide naam"
121 |
122 | #: models.py:107
123 | msgid "Name for this identity provider displayed on the login page."
124 | msgstr "Naam van de identiteit provider weergegeven op de login pagina."
125 |
126 | #: models.py:113 models.py:490
127 | msgid "position"
128 | msgstr "positie"
129 |
130 | #: models.py:127
131 | msgid "display"
132 | msgstr "weergave"
133 |
134 | #: models.py:128
135 | msgid "Display the provider on the login page."
136 | msgstr "geef de provider weer op de login pagina."
137 |
138 | #: models.py:166
139 | msgid "Federated user"
140 | msgstr "Gefedereerde gebruiker"
141 |
142 | #: models.py:167
143 | msgid "Federated users"
144 | msgstr "Gefedereerde gebruikers"
145 |
146 | #: models.py:246
147 | msgid "User attributes cache"
148 | msgstr "Gebruiker attributen cache"
149 |
150 | #: models.py:247
151 | msgid "User attributes caches"
152 | msgstr "Gebruiker attributen caches"
153 |
154 | #: models.py:271
155 | msgid "User"
156 | msgstr "Gebruiker"
157 |
158 | #: models.py:272
159 | msgid "Users"
160 | msgstr "Gebruikers"
161 |
162 | #: models.py:364
163 | #, python-format
164 | msgid "Error during service logout %s"
165 | msgstr "Een error is opgetreden tijdens het uitloggen %s"
166 |
167 | #: models.py:484
168 | msgid "Service pattern"
169 | msgstr "Service patroon"
170 |
171 | #: models.py:485
172 | msgid "Services patterns"
173 | msgstr "Service patronen"
174 |
175 | #: models.py:491
176 | msgid "service patterns are sorted using the position attribute"
177 | msgstr "service patronen worden gesorteerd aan de hand van de positie."
178 |
179 | #: models.py:499 models.py:664
180 | msgid "name"
181 | msgstr "naam"
182 |
183 | #: models.py:500
184 | msgid "A name for the service"
185 | msgstr "Een naam voor de service"
186 |
187 | #: models.py:508 models.py:707 models.py:737
188 | msgid "pattern"
189 | msgstr "patroon"
190 |
191 | #: models.py:510
192 | msgid ""
193 | "A regular expression matching services. Will usually looks like "
194 | "'^https://some\\.server\\.com/path/.*$'.As it is a regular expression, "
195 | "special character must be escaped with a '\\'."
196 | msgstr ""
197 | "A reguliere expressie die de matched met de services. Ziet er vaak zou uit: "
198 | "'^https://some\\.server\\.com/path/.*$'. Omdat het een reguliere expressie "
199 | "is, moeten speciale characters worden geescaped met een '\\'."
200 |
201 | #: models.py:521
202 | msgid "user field"
203 | msgstr "gebruiker veld"
204 |
205 | #: models.py:522
206 | msgid "Name of the attribute to transmit as username, empty = login"
207 | msgstr ""
208 | "Naam van het attribuut om verstuurd te worden al gebruikersnaam, leeg = "
209 | "login"
210 |
211 | #: models.py:527
212 | msgid "restrict username"
213 | msgstr "beperk gebruikersnamen"
214 |
215 | #: models.py:528
216 | msgid "Limit username allowed to connect to the list provided bellow"
217 | msgstr ""
218 | "Beperk de gebruikersnamen die deze provider mogen gebruiken met de list "
219 | "hieronder."
220 |
221 | #: models.py:533
222 | msgid "proxy"
223 | msgstr "proxy"
224 |
225 | #: models.py:534
226 | msgid "Proxy tickets can be delivered to the service"
227 | msgstr "Proxy tickets kunnen worden aangeleverd aan de service"
228 |
229 | #: models.py:540
230 | msgid "proxy callback"
231 | msgstr "proxy callback"
232 |
233 | #: models.py:541
234 | msgid "can be used as a proxy callback to deliver PGT"
235 | msgstr "kan gebruikt worden als proxy callback voor geleverde PGT"
236 |
237 | #: models.py:548
238 | msgid "single log out"
239 | msgstr "eenmalig afmelden"
240 |
241 | #: models.py:549
242 | msgid "Enable SLO for the service"
243 | msgstr "Activeer eenmalig afmelden voor de service"
244 |
245 | #: models.py:557
246 | msgid "single log out callback"
247 | msgstr "eenmalig afmelden callback"
248 |
249 | #: models.py:558
250 | msgid ""
251 | "URL where the SLO request will be POST. empty = service url\n"
252 | "This is usefull for non HTTP proxied services."
253 | msgstr ""
254 | "De URL waar de 'eenmalige afmelding' request naar wordt gepost. empty = service url\n"
255 | "Dit is handig voor niet HTTP geproxide services."
256 |
257 | #: models.py:639
258 | msgid "username allowed to connect to the service"
259 | msgstr ""
260 | "gebruikersnamen die zijn toegestaan om een connectie te maken via deze "
261 | "service."
262 |
263 | #: models.py:665
264 | msgid "name of an attribute to send to the service, use * for all attributes"
265 | msgstr ""
266 | "naam van een attribuut dat naar de service verstuurd wordt. Gebruik * voor "
267 | "alle attributen."
268 |
269 | #: models.py:672 models.py:745
270 | msgid "replace"
271 | msgstr "vervangen"
272 |
273 | #: models.py:673
274 | msgid ""
275 | "name under which the attribute will be show to the service. empty = default "
276 | "name of the attribut"
277 | msgstr ""
278 | "onder deze naam wordt het attribuut naar de service verstuurd. leeg = "
279 | "attribuut naam"
280 |
281 | #: models.py:700 models.py:731
282 | msgid "attribute"
283 | msgstr "attribuut"
284 |
285 | #: models.py:701
286 | msgid "Name of the attribute which must verify pattern"
287 | msgstr "Naam van het attribuut dat het patroon moet valideren."
288 |
289 | #: models.py:708
290 | msgid "a regular expression"
291 | msgstr "een reguliere expressie"
292 |
293 | #: models.py:732
294 | msgid "Name of the attribute for which the value must be replace"
295 | msgstr "Naam van het attribuut waarvoor de waarde moet worden vervangen"
296 |
297 | #: models.py:738
298 | msgid "An regular expression maching whats need to be replaced"
299 | msgstr "Een reguliere expressie dat matched wat vervangen moet worden"
300 |
301 | #: models.py:746
302 | msgid "replace expression, groups are capture by \\1, \\2 …"
303 | msgstr "vervang expressie, groepen zijn vast te leggen door \\1. \\2 ..."
304 |
305 | #: templates/cas_server/base.html:43
306 | #, python-format
307 | msgid ""
308 | "A new version of the application is available. This instance runs "
309 | "%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
310 | "upgrading."
311 | msgstr ""
312 | "Een nieuwe versie van de applicatie is beschikbaar. Deze applicatie draait "
313 | "op versie %(VERSION)s en de nieuwste versie is %(LAST_VERSION)s. Denk na "
314 | "over het uitvoegen van een upgrade."
315 |
316 | #: templates/cas_server/logged.html:4
317 | msgid ""
318 | "Log In Successful You have successfully logged into the Central "
319 | "Authentication Service. For security reasons, please Log Out and Exit "
320 | "your web browser when you are done accessing services that require "
321 | "authentication!"
322 | msgstr ""
323 | "Succesvol ingelogd. Je bent succesvol ingelogd op het CAS. Voor "
324 | "veiligheids-redenen, log uit en sluit de webbrowsers wanneer je klaar bent "
325 | "met de service!"
326 |
327 | #: templates/cas_server/logged.html:8
328 | msgid "Log me out from all my sessions"
329 | msgstr "Meld me af bij alle sessies"
330 |
331 | #: templates/cas_server/logged.html:14
332 | msgid "Forget the identity provider"
333 | msgstr "Vergeet de identiteits-provider"
334 |
335 | #: templates/cas_server/logged.html:18
336 | msgid "Logout"
337 | msgstr "Afmelden"
338 |
339 | #: templates/cas_server/login.html:6
340 | msgid "Please log in"
341 | msgstr "Log alstublieft in"
342 |
343 | #: templates/cas_server/login.html:14
344 | msgid "Login"
345 | msgstr "Log in"
346 |
347 | #: templates/cas_server/warn.html:9
348 | msgid "Connect to the service"
349 | msgstr "Verbind met de service"
350 |
351 | #: utils.py:744
352 | #, python-format
353 | msgid "\"%(value)s\" is not a valid regular expression"
354 | msgstr "\"%(value)s\" is geen geldige reguliere expressie"
355 |
356 | #: views.py:185
357 | msgid ""
358 | "Logout successful You have successfully logged out from the Central "
359 | "Authentication Service. For security reasons, close your web browser."
360 | msgstr ""
361 | "Succesvol afgemeld. Je bent succesvol afgemeld van de CAS. Voor "
362 | "veiligheids-redenen, sluit de webbrowser!"
363 |
364 | #: views.py:191
365 | #, python-format
366 | msgid ""
367 | "Logout successful You have successfully logged out from %s sessions "
368 | "of the Central Authentication Service. For security reasons, close your web "
369 | "browser."
370 | msgstr ""
371 | "Succesvol afgemeld. Je bent succesvol afgemeld van de %s "
372 | "sessie. Voor veiligheids-redenen, sluit de webbrowser!"
373 |
374 | #: views.py:198
375 | msgid ""
376 | "Logout successful You were already logged out from the Central "
377 | "Authentication Service. For security reasons, close your web browser."
378 | msgstr ""
379 | "Succesvol afgemeld. Je was al afgemeld van de CAS. Voor "
380 | "veiligheids-redenen, sluit de webbrowser!"
381 |
382 | #: views.py:378
383 | #, python-format
384 | msgid ""
385 | "Invalid response from your identity provider CAS upon ticket %(ticket)s "
386 | "validation: %(error)r"
387 | msgstr ""
388 | "Ongeldig response van de identiteit provider bij ticket %(ticket)s "
389 | "validatie: %(error)s"
390 |
391 | #: views.py:500
392 | msgid "Invalid login ticket, please try to log in again"
393 | msgstr "Ongeldig login ticket. probeer het alstublieft opnieuw"
394 |
395 | #: views.py:693
396 | #, python-format
397 | msgid "Authentication has been required by service %(name)s (%(url)s)"
398 | msgstr "service %(name)s (%(url)s) vraagt om authenticatie"
399 |
400 | #: views.py:731
401 | #, python-format
402 | msgid "Service %(url)s not allowed."
403 | msgstr "Service %(url)s is niet toegestaan."
404 |
405 | #: views.py:738
406 | msgid "Username not allowed"
407 | msgstr "Gebruikersnaam niet teogestaan"
408 |
409 | #: views.py:745
410 | msgid "User characteristics not allowed"
411 | msgstr "Gebruikers karakteristieken niet toegestaan."
412 |
413 | #: views.py:752
414 | #, python-format
415 | msgid "The attribute %(field)s is needed to use that service"
416 | msgstr "Het attribuut %(field)s is nodig voor die service"
417 |
418 | #: views.py:842
419 | #, python-format
420 | msgid "Authentication renewal required by service %(name)s (%(url)s)."
421 | msgstr ""
422 | "Service %(name)s (%(url)s) vraagt om vernieuwing van de authenticatie."
423 |
424 | #: views.py:849
425 | #, python-format
426 | msgid "Authentication required by service %(name)s (%(url)s)."
427 | msgstr "Service %(name)s (%(url)s) vraagt om authenticatie"
428 |
429 | #: views.py:856
430 | #, python-format
431 | msgid "Service %s not allowed"
432 | msgstr "Service %s niet toegestaan"
433 |
--------------------------------------------------------------------------------
/cas_server/default_settings.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This program is distributed in the hope that it will be useful, but WITHOUT
3 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
5 | # more details.
6 | #
7 | # You should have received a copy of the GNU General Public License version 3
8 | # along with this program; if not, write to the Free Software Foundation, Inc., 51
9 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
10 | #
11 | # (c) 2015-2025 Valentin Samir
12 | """Default values for the app's settings"""
13 | from django.conf import settings
14 | from django.templatetags.static import static
15 |
16 | from importlib import import_module
17 |
18 | import sys
19 | if sys.version_info < (3, ):
20 | from django.utils.translation import ugettext_lazy as _
21 | else:
22 | from django.utils.translation import gettext_lazy as _
23 |
24 |
25 | try:
26 | #: URL to the logo showed in the up left corner on the default templates.
27 | CAS_LOGO_URL = static("cas_server/logo.png")
28 | #: URL to the favicon (shortcut icon) used by the default templates. Default is a key icon.
29 | CAS_FAVICON_URL = static("cas_server/favicon.ico")
30 | # is settings.DEBUG is False and collectstatics has not been run yet, the static function will
31 | # raise a ValueError because the file is not found.
32 | except ValueError:
33 | #: URL to the logo showed in the up left corner on the default templates.
34 | CAS_LOGO_URL = None
35 | #: URL to the favicon (shortcut icon) used by the default templates. Default is a key icon.
36 | CAS_FAVICON_URL = None
37 |
38 |
39 | #: Show the powered by footer if set to ``True``
40 | CAS_SHOW_POWERED = True
41 | #: URLs to css and javascript external components.
42 | CAS_COMPONENT_URLS = {
43 | "bootstrap3_css": "https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/css/bootstrap.min.css",
44 | "bootstrap3_js": "https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/js/bootstrap.min.js",
45 | "html5shiv": "https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js",
46 | "respond": "https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js",
47 | "bootstrap4_css": "https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css",
48 | "bootstrap4_js": "https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js",
49 | "jquery": "https://code.jquery.com/jquery.min.js",
50 | }
51 | #: Path to the template showed on /login then the user is not autenticated.
52 | CAS_LOGIN_TEMPLATE = 'cas_server/bs4/login.html'
53 | #: Path to the template showed on /login?service=... then the user is authenticated and has asked
54 | #: to be warned before being connected to a service.
55 | CAS_WARN_TEMPLATE = 'cas_server/bs4/warn.html'
56 | #: Path to the template showed on /login then to user is authenticated.
57 | CAS_LOGGED_TEMPLATE = 'cas_server/bs4/logged.html'
58 | #: Path to the template showed on /logout then to user is being disconnected.
59 | CAS_LOGOUT_TEMPLATE = 'cas_server/bs4/logout.html'
60 | #: Should we redirect users to /login after they logged out instead of displaying
61 | #: :obj:`CAS_LOGOUT_TEMPLATE`.
62 | CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT = False
63 |
64 |
65 | #: A dotted path to a class or a class implementing cas_server.auth.AuthUser.
66 | CAS_AUTH_CLASS = 'cas_server.auth.DjangoAuthUser'
67 | #: Activate Kerberos authentication (not compatible with federate mode or auth class
68 | #: requiring access to the user credential to retrieve user attributes).
69 | #: See https://web.mit.edu/kerberos/krb5-1.13/doc/admin/env_variables.html
70 | #: for environment variables allowing to configure the underlying GSSAPI C library
71 | #: Username retrieved form kerberos auth MUST match username used by the ``CAS_AUTH_CLASS``
72 | CAS_AUTH_GSSAPI_ENABLE = False
73 | #: SPN to use for Kerberos authentication (must be available in the loaded keytab)
74 | CAS_AUTH_GSSAPI_SERVICENAME = "host/myhost.example.com@AD.EXAMPLE.COM"
75 | #: Path to certificate authorities file. Usually on linux the local CAs are in
76 | #: /etc/ssl/certs/ca-certificates.crt. ``True`` tell requests to use its internal certificat
77 | #: authorities.
78 | CAS_PROXY_CA_CERTIFICATE_PATH = True
79 | #: Maximum number of parallel single log out requests send
80 | #: if more requests need to be send, there are queued
81 | CAS_SLO_MAX_PARALLEL_REQUESTS = 10
82 | #: Timeout for a single SLO request in seconds.
83 | CAS_SLO_TIMEOUT = 5
84 | #: Shared to transmit then using the view :class:`cas_server.views.Auth`
85 | CAS_AUTH_SHARED_SECRET = ''
86 | #: Max time after with the user MUST reauthenticate. Let it to `None` for no max time.
87 | #: This can be used to force refreshing cached informations only available upon user authentication
88 | #: like the user attributes in federation mode or with the ldap auth in bind mode.
89 | CAS_TGT_VALIDITY = None
90 |
91 |
92 | #: Number of seconds the service tickets and proxy tickets are valid. This is the maximal time
93 | #: between ticket issuance by the CAS and ticket validation by an application.
94 | CAS_TICKET_VALIDITY = 60
95 | #: Number of seconds the proxy granting tickets are valid.
96 | CAS_PGT_VALIDITY = 3600
97 | #: Number of seconds a ticket is kept in the database before sending Single Log Out request and
98 | #: being cleared.
99 | CAS_TICKET_TIMEOUT = 24*3600
100 |
101 |
102 | #: All CAS implementation MUST support ST and PT up to 32 chars,
103 | #: PGT and PGTIOU up to 64 chars and it is RECOMMENDED that all
104 | #: tickets up to 256 chars are supports so we use 64 for the default
105 | #: len.
106 | CAS_TICKET_LEN = 64
107 |
108 | #: alias of :obj:`settings.CAS_TICKET_LEN`
109 | CAS_LT_LEN = getattr(settings, 'CAS_TICKET_LEN', CAS_TICKET_LEN)
110 | #: alias of :obj:`settings.CAS_TICKET_LEN`
111 | #: Services MUST be able to accept service tickets of up to 32 characters in length.
112 | CAS_ST_LEN = getattr(settings, 'CAS_TICKET_LEN', CAS_TICKET_LEN)
113 | #: alias of :obj:`settings.CAS_TICKET_LEN`
114 | #: Back-end services MUST be able to accept proxy tickets of up to 32 characters.
115 | CAS_PT_LEN = getattr(settings, 'CAS_TICKET_LEN', CAS_TICKET_LEN)
116 | #: alias of :obj:`settings.CAS_TICKET_LEN`
117 | #: Services MUST be able to handle proxy-granting tickets of up to 64
118 | CAS_PGT_LEN = getattr(settings, 'CAS_TICKET_LEN', CAS_TICKET_LEN)
119 | #: alias of :obj:`settings.CAS_TICKET_LEN`
120 | #: Services MUST be able to handle PGTIOUs of up to 64 characters in length.
121 | CAS_PGTIOU_LEN = getattr(settings, 'CAS_TICKET_LEN', CAS_TICKET_LEN)
122 |
123 | #: Prefix of login tickets.
124 | CAS_LOGIN_TICKET_PREFIX = u'LT'
125 | #: Prefix of service tickets. Service tickets MUST begin with the characters ST so you should not
126 | #: change this.
127 | CAS_SERVICE_TICKET_PREFIX = u'ST'
128 | #: Prefix of proxy ticket. Proxy tickets SHOULD begin with the characters, PT.
129 | CAS_PROXY_TICKET_PREFIX = u'PT'
130 | #: Prefix of proxy granting ticket. Proxy-granting tickets SHOULD begin with the characters PGT.
131 | CAS_PROXY_GRANTING_TICKET_PREFIX = u'PGT'
132 | #: Prefix of proxy granting ticket IOU. Proxy-granting ticket IOUs SHOULD begin with the characters
133 | #: PGTIOU.
134 | CAS_PROXY_GRANTING_TICKET_IOU_PREFIX = u'PGTIOU'
135 |
136 |
137 | #: Host for the SQL server.
138 | CAS_SQL_HOST = 'localhost'
139 | #: Username for connecting to the SQL server.
140 | CAS_SQL_USERNAME = ''
141 | #: Password for connecting to the SQL server.
142 | CAS_SQL_PASSWORD = ''
143 | #: Database name.
144 | CAS_SQL_DBNAME = ''
145 | #: Database charset.
146 | CAS_SQL_DBCHARSET = 'utf8'
147 |
148 | #: The query performed upon user authentication.
149 | CAS_SQL_USER_QUERY = 'SELECT user AS username, pass AS password, users.* FROM users WHERE user = %s'
150 | #: The method used to check the user password. Must be one of ``"crypt"``, ``"ldap"``,
151 | #: ``"hex_md5"``, ``"hex_sha1"``, ``"hex_sha224"``, ``"hex_sha256"``, ``"hex_sha384"``,
152 | #: ``"hex_sha512"``, ``"plain"``.
153 | CAS_SQL_PASSWORD_CHECK = 'crypt'
154 | #: charset the SQL users passwords was hash with
155 | CAS_SQL_PASSWORD_CHARSET = "utf-8"
156 |
157 |
158 | #: Address of the LDAP server
159 | CAS_LDAP_SERVER = 'localhost'
160 | #: LDAP user bind address, for example ``"cn=admin,dc=crans,dc=org"`` for connecting to the LDAP
161 | #: server.
162 | CAS_LDAP_USER = None
163 | #: LDAP connection password
164 | CAS_LDAP_PASSWORD = None
165 | #: LDAP seach base DN, for example ``"ou=data,dc=crans,dc=org"``.
166 | CAS_LDAP_BASE_DN = None
167 | #: LDAP search filter for searching user by username. User inputed usernames are escaped using
168 | #: :func:`ldap3.utils.conv.escape_bytes`.
169 | CAS_LDAP_USER_QUERY = "(uid=%(username)s)"
170 | #: LDAP attribute used for users usernames
171 | CAS_LDAP_USERNAME_ATTR = "uid"
172 | #: LDAP attribute used for users passwords
173 | CAS_LDAP_PASSWORD_ATTR = "userPassword"
174 | #: The method used to check the user password. Must be one of ``"crypt"``, ``"ldap"``,
175 | #: ``"hex_md5"``, ``"hex_sha1"``, ``"hex_sha224"``, ``"hex_sha256"``, ``"hex_sha384"``,
176 | #: ``"hex_sha512"``, ``"plain"``, ``"bind"``.
177 | CAS_LDAP_PASSWORD_CHECK = "ldap"
178 | #: charset the LDAP users passwords was hash with
179 | CAS_LDAP_PASSWORD_CHARSET = "utf-8"
180 | #: This parameter is only used then ``CAS_LDAP_PASSWORD_CHECK`` is set to ``"bind"``.
181 | #:
182 | #: * if ``0`` the user attributes are retrieved by connecting to the ldap as ``CAS_LDAP_USER``.
183 | #: * if ``1`` the user attributes are retrieve then the user authenticate using
184 | #: the user credentials. These attributes are then cached for the session.
185 | #:
186 | #: The default is ``0``.
187 | CAS_LDAP_ATTRS_VIEW = 0
188 |
189 |
190 | #: Username of the test user.
191 | CAS_TEST_USER = 'test'
192 | #: Password of the test user.
193 | CAS_TEST_PASSWORD = 'test'
194 | #: Attributes of the test user.
195 | CAS_TEST_ATTRIBUTES = {
196 | 'nom': 'Nymous',
197 | 'prenom': 'Ano',
198 | 'email': 'anonymous@example.net',
199 | 'alias': ['demo1', 'demo2']
200 | }
201 |
202 |
203 | #: A :class:`bool` for activatinc the hability to fetch tickets using javascript.
204 | CAS_ENABLE_AJAX_AUTH = False
205 |
206 |
207 | #: A :class:`bool` for activating the federated mode
208 | CAS_FEDERATE = False
209 | #: Time after witch the cookie use for “remember my identity provider” expire (one week).
210 | CAS_FEDERATE_REMEMBER_TIMEOUT = 604800
211 |
212 | #: A :class:`bool` for diplaying a warning on html pages then a new version of the application
213 | #: is avaible. Once closed by a user, it is not displayed to this user until the next new version.
214 | CAS_NEW_VERSION_HTML_WARNING = True
215 | #: A :class:`bool` for sending emails to ``settings.ADMINS`` when a new version is available.
216 | CAS_NEW_VERSION_EMAIL_WARNING = True
217 | #: URL to the pypi json of the application. Used to retreive the version number of the last version.
218 | #: You should not change it.
219 | CAS_NEW_VERSION_JSON_URL = "https://pypi.org/pypi/django-cas-server/json"
220 |
221 | #: If the service message should be displayed on the login page
222 | CAS_SHOW_SERVICE_MESSAGES = True
223 |
224 | #: Messages displayed in a info-box on the html pages of the default templates.
225 | #: ``CAS_INFO_MESSAGES`` is a :class:`dict` mapping message name to a message :class:`dict`.
226 | #: A message :class:`dict` has 3 keys:
227 | #:
228 | #: * ``message``: A :class:`unicode`, the message to display, potentially wrapped around
229 | #: ugettex_lazy
230 | #: * ``discardable``: A :class:`bool`, specify if the users can close the message info-box
231 | #: * ``type``: One of info, success, info, warning, danger. The type of the info-box.
232 | #:
233 | #: ``CAS_INFO_MESSAGES`` contains by default one message, ``cas_explained``, which explain
234 | #: roughly the purpose of a CAS.
235 | CAS_INFO_MESSAGES = {
236 | "cas_explained": {
237 | "message": _(
238 | u"The Central Authentication Service grants you access to most of our websites by "
239 | u"authenticating only once, so you don't need to type your credentials again unless "
240 | u"your session expires or you logout."
241 | ),
242 | "discardable": True,
243 | "type": "info", # one of info, success, info, warning, danger
244 | },
245 | }
246 | #: :class:`list` of message names. Order in which info-box messages are displayed.
247 | #: Let the list empty to disable messages display.
248 | CAS_INFO_MESSAGES_ORDER = []
249 |
250 | #: :class:`bool` If `True` Django session cookie will be removed on logout from CAS server
251 | CAS_REMOVE_DJANGO_SESSION_COOKIE_ON_LOGOUT = False
252 | #: :class:`bool` If `True` Django csrf cookie will be removed on logout from CAS server
253 | CAS_REMOVE_DJANGO_CSRF_COOKIE_ON_LOGOUT = False
254 | #: :class:`bool` If `True` Django language cookie will be removed on logout from CAS server
255 | CAS_REMOVE_DJANGO_LANGUAGE_COOKIE_ON_LOGOUT = False
256 |
257 | #: A dotted path to a form or a form used on the login page to retrieve user credentials
258 | CAS_USER_CREDENTIAL_FORM = "cas_server.forms.UserCredential"
259 | #: A dotted path to a form or a form used on warn page before emitting a ticket
260 | CAS_WARN_FORM = "cas_server.forms.WarnForm"
261 | #: A dotted path to a form or a form used on the login page to select another CAS in federated mode
262 | CAS_FEDERATE_SELECT_FORM = "cas_server.forms.FederateSelect"
263 | #: A dotted path to a form or a form used on the login page in federated mode
264 | CAS_FEDERATE_USER_CREDENTIAL_FORM = "cas_server.forms.FederateUserCredential"
265 | #: A dotted path to a form or a form for Tickets in the admin interface
266 | CAS_TICKET_FORM = "cas_server.forms.TicketForm"
267 |
268 | GLOBALS = globals().copy()
269 | for name, default_value in GLOBALS.items():
270 | # only care about parameter begining by CAS_
271 | if name.startswith("CAS_"):
272 | # get the current setting value, falling back to default_value
273 | value = getattr(settings, name, default_value)
274 | # set the setting value to its value if defined, else to the default_value.
275 | setattr(settings, name, value)
276 |
277 | # Allow the user defined CAS_COMPONENT_URLS to omit not changed values
278 | MERGED_CAS_COMPONENT_URLS = CAS_COMPONENT_URLS.copy()
279 | MERGED_CAS_COMPONENT_URLS.update(settings.CAS_COMPONENT_URLS)
280 | settings.CAS_COMPONENT_URLS = MERGED_CAS_COMPONENT_URLS
281 |
282 | # if the federated mode is enabled, we must use the :class`cas_server.auth.CASFederateAuth` auth
283 | # backend.
284 | if settings.CAS_FEDERATE:
285 | settings.CAS_AUTH_CLASS = "cas_server.auth.CASFederateAuth"
286 |
287 |
288 | #: SessionStore class depending of :django:setting:`SESSION_ENGINE`
289 | SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
290 |
--------------------------------------------------------------------------------