Merge pull request #83 from nitmir/dev
Update to version 2.0.0 v2.0.0 - 2022-10-17 =================== Added ----- * Support for Django 4.0 and 4.1 * Add locale for zh_Hans * Add a unit test with a non ascii char in service url * Add settings to allow deletings Django cookies upon logout Changed ------- * Update CI: require pytest >= 7 and remove pytest-pythonpath dependancy Fixes ----- * Fix unicode sandwich issue in cas_server.utils.update_url * Fix DeprecationWarning about default_app_config in Django 3.2 * Fix DeprecationWarning about USE_L10N in Django 4.0 Removed ------- * Drop support for python 2.7 (now deprecated for more than 2 years, expect it to break now or in a near future) * Drop support for python 3.5 (but it should keep working for a while. pytest >= 7 do not support python 3.5 and Debian Stretch support ended)
This commit is contained in:
commit
0025a3772b
17 changed files with 601 additions and 54 deletions
23
.travis.yml
23
.travis.yml
|
@ -1,3 +1,4 @@
|
||||||
|
dist: focal
|
||||||
language: python
|
language: python
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
|
@ -8,13 +9,7 @@ matrix:
|
||||||
env: TOX_ENV=check_rst
|
env: TOX_ENV=check_rst
|
||||||
- python: "3.9"
|
- python: "3.9"
|
||||||
env: TOX_ENV=coverage
|
env: TOX_ENV=coverage
|
||||||
# Debian strech support
|
# REHL 7 support and Ubuntu bionic
|
||||||
- python: "3.5"
|
|
||||||
env: TOX_ENV=py35-django111
|
|
||||||
- python: "3.5"
|
|
||||||
env: TOX_ENV=py35-django111
|
|
||||||
arch: ppc64le
|
|
||||||
# Ubuntu bionic and EPEL 7 support
|
|
||||||
- python: "3.6"
|
- python: "3.6"
|
||||||
env: TOX_ENV=py36-django111
|
env: TOX_ENV=py36-django111
|
||||||
- python: "3.6"
|
- python: "3.6"
|
||||||
|
@ -38,17 +33,25 @@ matrix:
|
||||||
- python: "3.8"
|
- python: "3.8"
|
||||||
env: TOX_ENV=py38-django22
|
env: TOX_ENV=py38-django22
|
||||||
arch: ppc64le
|
arch: ppc64le
|
||||||
# Debian bullseye and Ubuntu hirsute support
|
# Debian bullseye and Ubuntu hirsute and impish support
|
||||||
- python: "3.9"
|
- python: "3.9"
|
||||||
env: TOX_ENV=py39-django22
|
env: TOX_ENV=py39-django22
|
||||||
- python: "3.9"
|
- python: "3.9"
|
||||||
env: TOX_ENV=py39-django22
|
env: TOX_ENV=py39-django22
|
||||||
arch: ppc64le
|
arch: ppc64le
|
||||||
|
# Ubuntu jammy and kinetic support
|
||||||
|
- python: "3.10"
|
||||||
|
env: TOX_ENV=py310-django32
|
||||||
|
- python: "3.10"
|
||||||
|
env: TOX_ENV=py310-django32
|
||||||
|
arch: ppc64le
|
||||||
# Django additional supported version
|
# Django additional supported version
|
||||||
- python: "3.9"
|
|
||||||
env: TOX_ENV=py39-django31
|
|
||||||
- python: "3.9"
|
- python: "3.9"
|
||||||
env: TOX_ENV=py39-django32
|
env: TOX_ENV=py39-django32
|
||||||
|
- python: "3.10"
|
||||||
|
env: TOX_ENV=py310-django40
|
||||||
|
- python: "3.10"
|
||||||
|
env: TOX_ENV=py310-django41
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
|
|
|
@ -6,6 +6,34 @@ All notable changes to this project will be documented in this file.
|
||||||
.. contents:: Table of Contents
|
.. contents:: Table of Contents
|
||||||
:depth: 2
|
:depth: 2
|
||||||
|
|
||||||
|
v2.0.0 - 2022-10-17
|
||||||
|
===================
|
||||||
|
|
||||||
|
Added
|
||||||
|
-----
|
||||||
|
* Support for Django 4.0 and 4.1
|
||||||
|
* Add locale for zh_Hans
|
||||||
|
* Add a unit test with a non ascii char in service url
|
||||||
|
* Add settings to allow deletings Django cookies upon logout
|
||||||
|
|
||||||
|
Changed
|
||||||
|
-------
|
||||||
|
* Update CI: require pytest >= 7 and remove pytest-pythonpath dependancy
|
||||||
|
|
||||||
|
Fixes
|
||||||
|
-----
|
||||||
|
* Fix unicode sandwich issue in cas_server.utils.update_url
|
||||||
|
* Fix DeprecationWarning about default_app_config in Django 3.2
|
||||||
|
* Fix DeprecationWarning about USE_L10N in Django 4.0
|
||||||
|
|
||||||
|
Removed
|
||||||
|
-------
|
||||||
|
* Drop support for python 2.7 (now deprecated for more than 2 years,
|
||||||
|
expect it to break now or in a near future)
|
||||||
|
* Drop support for python 3.5 (but it should keep working for a while.
|
||||||
|
pytest >= 7 do not support python 3.5 and Debian Stretch support ended)
|
||||||
|
|
||||||
|
|
||||||
v1.3.1 - 2021-07-03
|
v1.3.1 - 2021-07-03
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
|
17
README.rst
17
README.rst
|
@ -21,15 +21,15 @@ Features
|
||||||
* Possibility to rename/rewrite attributes per service
|
* Possibility to rename/rewrite attributes per service
|
||||||
* Possibility to require some attribute values per service
|
* Possibility to require some attribute values per service
|
||||||
* Federated mode between multiple CAS
|
* Federated mode between multiple CAS
|
||||||
* Supports Django 1.11, 2.2, 3.1 and 3.2
|
* Supports Django 1.11, 2.2, 3.2, 4.0 and 4.1
|
||||||
* Supports Python 3.5+
|
* Supports Python 3.6+
|
||||||
|
|
||||||
Dependencies
|
Dependencies
|
||||||
============
|
============
|
||||||
|
|
||||||
``django-cas-server`` depends on the following python packages:
|
``django-cas-server`` depends on the following python packages:
|
||||||
|
|
||||||
* Django >= 1.11 < 3.3
|
* Django >= 1.11 < 4.2
|
||||||
* requests >= 2.4
|
* requests >= 2.4
|
||||||
* requests_futures >= 0.9.5
|
* requests_futures >= 0.9.5
|
||||||
* lxml >= 3.4
|
* lxml >= 3.4
|
||||||
|
@ -285,6 +285,17 @@ Authentication settings
|
||||||
|
|
||||||
* ``CAS_SLO_TIMEOUT``: Timeout for a single SLO request in seconds. The default is ``5``.
|
* ``CAS_SLO_TIMEOUT``: Timeout for a single SLO request in seconds. The default is ``5``.
|
||||||
|
|
||||||
|
* ``CAS_REMOVE_DJANGO_SESSION_COOKIE_ON_LOGOUT``: If `True` Django session cookie will be removed
|
||||||
|
on logout from CAS server (default `False`). Note that Django session middleware will generate
|
||||||
|
a new session cookie.
|
||||||
|
|
||||||
|
* ``CAS_REMOVE_DJANGO_CSRF_COOKIE_ON_LOGOUT``: If `True` Django csrf cookie will be removed on
|
||||||
|
logout from CAS server (default `False`). Note that Django csrf middleware will generate a new
|
||||||
|
csrf token cookie.
|
||||||
|
|
||||||
|
* ``CAS_REMOVE_DJANGO_LANGUAGE_COOKIE_ON_LOGOUT``: If `True` Django language cookie will be
|
||||||
|
removed on logout from CAS server (default `False`).
|
||||||
|
|
||||||
|
|
||||||
Federation settings
|
Federation settings
|
||||||
-------------------
|
-------------------
|
||||||
|
|
|
@ -9,9 +9,14 @@
|
||||||
#
|
#
|
||||||
# (c) 2015-2016 Valentin Samir
|
# (c) 2015-2016 Valentin Samir
|
||||||
"""A django CAS server application"""
|
"""A django CAS server application"""
|
||||||
|
try:
|
||||||
|
import django
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
django = None
|
||||||
|
|
||||||
#: version of the application
|
#: version of the application
|
||||||
VERSION = '1.3.1'
|
VERSION = '2.0.0'
|
||||||
|
|
||||||
|
if django is None or django.VERSION < (3, 2):
|
||||||
#: path the the application configuration class
|
#: path the the application configuration class
|
||||||
default_app_config = 'cas_server.apps.CasAppConfig'
|
default_app_config = 'cas_server.apps.CasAppConfig'
|
||||||
|
|
|
@ -239,6 +239,13 @@ CAS_INFO_MESSAGES = {
|
||||||
#: Let the list empty to disable messages display.
|
#: Let the list empty to disable messages display.
|
||||||
CAS_INFO_MESSAGES_ORDER = []
|
CAS_INFO_MESSAGES_ORDER = []
|
||||||
|
|
||||||
|
#: :class:`bool` If `True` Django session cookie will be removed on logout from CAS server
|
||||||
|
CAS_REMOVE_DJANGO_SESSION_COOKIE_ON_LOGOUT = False
|
||||||
|
#: :class:`bool` If `True` Django csrf cookie will be removed on logout from CAS server
|
||||||
|
CAS_REMOVE_DJANGO_CSRF_COOKIE_ON_LOGOUT = False
|
||||||
|
#: :class:`bool` If `True` Django language cookie will be removed on logout from CAS server
|
||||||
|
CAS_REMOVE_DJANGO_LANGUAGE_COOKIE_ON_LOGOUT = False
|
||||||
|
|
||||||
|
|
||||||
GLOBALS = globals().copy()
|
GLOBALS = globals().copy()
|
||||||
for name, default_value in GLOBALS.items():
|
for name, default_value in GLOBALS.items():
|
||||||
|
|
BIN
cas_server/locale/zh_Hans/LC_MESSAGES/django.mo
Normal file
BIN
cas_server/locale/zh_Hans/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
421
cas_server/locale/zh_Hans/LC_MESSAGES/django.po
Normal file
421
cas_server/locale/zh_Hans/LC_MESSAGES/django.po
Normal file
|
@ -0,0 +1,421 @@
|
||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2021-08-01 22:18+0800\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
|
|
||||||
|
#: apps.py:30 templates/cas_server/bs3/base.html:7
|
||||||
|
#: templates/cas_server/bs3/base.html:26 templates/cas_server/bs4/base.html:6
|
||||||
|
#: templates/cas_server/bs4/base.html:17
|
||||||
|
msgid "Central Authentication Service"
|
||||||
|
msgstr "认证中心服务"
|
||||||
|
|
||||||
|
#: default_settings.py:230
|
||||||
|
msgid ""
|
||||||
|
"The Central Authentication Service grants you access to most of our websites "
|
||||||
|
"by authenticating only once, so you don't need to type your credentials "
|
||||||
|
"again unless your session expires or you logout."
|
||||||
|
msgstr ""
|
||||||
|
"您仅需在认证中心认证一次,就可以访问您的多数网站, "
|
||||||
|
"这样您不再需要重复输入认证,除非您的会话过期,或者您登出了."
|
||||||
|
|
||||||
|
#: forms.py:93
|
||||||
|
msgid "Identity provider"
|
||||||
|
msgstr "身份提供者"
|
||||||
|
|
||||||
|
#: forms.py:97 forms.py:119
|
||||||
|
msgid "Warn me before logging me into other sites."
|
||||||
|
msgstr "登录到其它网站时警告我"
|
||||||
|
|
||||||
|
#: forms.py:101
|
||||||
|
msgid "Remember the identity provider"
|
||||||
|
msgstr "记住此身份提供者"
|
||||||
|
|
||||||
|
#: forms.py:112 models.py:646
|
||||||
|
msgid "username"
|
||||||
|
msgstr "用户名"
|
||||||
|
|
||||||
|
#: forms.py:116
|
||||||
|
msgid "password"
|
||||||
|
msgstr "密码"
|
||||||
|
|
||||||
|
#: forms.py:139
|
||||||
|
msgid "The credentials you provided cannot be determined to be authentic."
|
||||||
|
msgstr "您提供的令牌不能通过鉴权"
|
||||||
|
|
||||||
|
#: forms.py:191
|
||||||
|
msgid "User not found in the temporary database, please try to reconnect"
|
||||||
|
msgstr "在临时数据库找不到此用户,请尝试重新连接"
|
||||||
|
|
||||||
|
#: forms.py:205
|
||||||
|
msgid "service"
|
||||||
|
msgstr "服务"
|
||||||
|
|
||||||
|
#: management/commands/cas_clean_federate.py:25
|
||||||
|
msgid "Clean old federated users"
|
||||||
|
msgstr "清除过期联盟用户"
|
||||||
|
|
||||||
|
#: management/commands/cas_clean_sessions.py:27
|
||||||
|
msgid "Clean deleted sessions"
|
||||||
|
msgstr "清除被删除的会话"
|
||||||
|
|
||||||
|
#: management/commands/cas_clean_tickets.py:27
|
||||||
|
msgid "Clean old tickets"
|
||||||
|
msgstr "清除过期凭证"
|
||||||
|
|
||||||
|
#: models.py:79
|
||||||
|
msgid "identity provider"
|
||||||
|
msgstr "身份提供者"
|
||||||
|
|
||||||
|
#: models.py:80
|
||||||
|
msgid "identity providers"
|
||||||
|
msgstr "身份证供者"
|
||||||
|
|
||||||
|
#: models.py:86
|
||||||
|
msgid "suffix"
|
||||||
|
msgstr "后缀"
|
||||||
|
|
||||||
|
#: models.py:88
|
||||||
|
msgid ""
|
||||||
|
"Suffix append to backend CAS returned username: ``returned_username`` @ "
|
||||||
|
"``suffix``."
|
||||||
|
msgstr "后端 CAS 附加后缀返回的用户名: ``returned_username`` @ "
|
||||||
|
"``suffix``."
|
||||||
|
|
||||||
|
#: models.py:95
|
||||||
|
msgid "server url"
|
||||||
|
msgstr "服务 url"
|
||||||
|
|
||||||
|
#: models.py:105
|
||||||
|
msgid "CAS protocol version"
|
||||||
|
msgstr "CAS 协议版本"
|
||||||
|
|
||||||
|
#: models.py:107
|
||||||
|
msgid ""
|
||||||
|
"Version of the CAS protocol to use when sending requests the the backend CAS."
|
||||||
|
msgstr ""
|
||||||
|
"后端 CAS 发送请求时使用的 CAS 协议版本"
|
||||||
|
|
||||||
|
#: models.py:114
|
||||||
|
msgid "verbose name"
|
||||||
|
msgstr "详细名称"
|
||||||
|
|
||||||
|
#: models.py:115
|
||||||
|
msgid "Name for this identity provider displayed on the login page."
|
||||||
|
msgstr "在登录页显示的身份提供者的名字"
|
||||||
|
|
||||||
|
#: models.py:121 models.py:498
|
||||||
|
msgid "position"
|
||||||
|
msgstr "位置"
|
||||||
|
|
||||||
|
#: models.py:135
|
||||||
|
msgid "display"
|
||||||
|
msgstr "显示"
|
||||||
|
|
||||||
|
#: models.py:136
|
||||||
|
msgid "Display the provider on the login page."
|
||||||
|
msgstr "在登录页显示提供者"
|
||||||
|
|
||||||
|
#: models.py:174
|
||||||
|
msgid "Federated user"
|
||||||
|
msgstr "联盟用户"
|
||||||
|
|
||||||
|
#: models.py:175
|
||||||
|
msgid "Federated users"
|
||||||
|
msgstr "联盟用户"
|
||||||
|
|
||||||
|
#: models.py:254
|
||||||
|
msgid "User attributes cache"
|
||||||
|
msgstr "用户属性缓存"
|
||||||
|
|
||||||
|
#: models.py:255
|
||||||
|
msgid "User attributes caches"
|
||||||
|
msgstr "用户属性缓存"
|
||||||
|
|
||||||
|
#: models.py:279
|
||||||
|
msgid "User"
|
||||||
|
msgstr "用户"
|
||||||
|
|
||||||
|
#: models.py:280
|
||||||
|
msgid "Users"
|
||||||
|
msgstr "用户"
|
||||||
|
|
||||||
|
#: models.py:372
|
||||||
|
#, python-format
|
||||||
|
msgid "Error during service logout %s"
|
||||||
|
msgstr "服务登出中的异常 %s"
|
||||||
|
|
||||||
|
#: models.py:492
|
||||||
|
msgid "Service pattern"
|
||||||
|
msgstr "服务范式"
|
||||||
|
|
||||||
|
#: models.py:493
|
||||||
|
msgid "Services patterns"
|
||||||
|
msgstr "服务范式"
|
||||||
|
|
||||||
|
#: models.py:499
|
||||||
|
msgid "service patterns are sorted using the position attribute"
|
||||||
|
msgstr "服务范式会按照位置属性排序"
|
||||||
|
|
||||||
|
#: models.py:507 models.py:676
|
||||||
|
msgid "name"
|
||||||
|
msgstr "名称"
|
||||||
|
|
||||||
|
#: models.py:508
|
||||||
|
msgid "A name for the service"
|
||||||
|
msgstr "服务的名称"
|
||||||
|
|
||||||
|
#: models.py:516 models.py:723 models.py:757
|
||||||
|
msgid "pattern"
|
||||||
|
msgstr "范式"
|
||||||
|
|
||||||
|
#: models.py:518
|
||||||
|
msgid ""
|
||||||
|
"A regular expression matching services. Will usually looks like '^https://"
|
||||||
|
"some\\.server\\.com/path/.*$'.As it is a regular expression, special "
|
||||||
|
"character must be escaped with a '\\'."
|
||||||
|
msgstr ""
|
||||||
|
"用一个正则表示式来匹配服务。一般如 '^https://"
|
||||||
|
"some\\.server\\.com/path/.*$'. 在正则表达式中,特殊"
|
||||||
|
"字符必须用 '\\' 转码."
|
||||||
|
|
||||||
|
#: models.py:529
|
||||||
|
msgid "user field"
|
||||||
|
msgstr "用户字段"
|
||||||
|
|
||||||
|
#: models.py:530
|
||||||
|
msgid "Name of the attribute to transmit as username, empty = login"
|
||||||
|
msgstr "被转译作为用户名的属性字段,空 = login"
|
||||||
|
|
||||||
|
#: models.py:535
|
||||||
|
msgid "restrict username"
|
||||||
|
msgstr "用户名限制"
|
||||||
|
|
||||||
|
#: models.py:536
|
||||||
|
msgid "Limit username allowed to connect to the list provided bellow"
|
||||||
|
msgstr "只允许下面列表提供的用户名连接"
|
||||||
|
|
||||||
|
#: models.py:541
|
||||||
|
msgid "proxy"
|
||||||
|
msgstr "代理"
|
||||||
|
|
||||||
|
#: models.py:542
|
||||||
|
msgid "Proxy tickets can be delivered to the service"
|
||||||
|
msgstr "可以对服务分发的代理凭证"
|
||||||
|
|
||||||
|
#: models.py:548
|
||||||
|
msgid "proxy callback"
|
||||||
|
msgstr "代理回调"
|
||||||
|
|
||||||
|
#: models.py:549
|
||||||
|
msgid "can be used as a proxy callback to deliver PGT"
|
||||||
|
msgstr "可以作为代理回调来分发PGT"
|
||||||
|
|
||||||
|
#: models.py:556
|
||||||
|
msgid "single log out"
|
||||||
|
msgstr "单点登出"
|
||||||
|
|
||||||
|
#: models.py:557
|
||||||
|
msgid "Enable SLO for the service"
|
||||||
|
msgstr "为服务启用 SLO"
|
||||||
|
|
||||||
|
#: models.py:565
|
||||||
|
msgid "single log out callback"
|
||||||
|
msgstr "单点登出回调"
|
||||||
|
|
||||||
|
#: models.py:566
|
||||||
|
msgid ""
|
||||||
|
"URL where the SLO request will be POST. empty = service url\n"
|
||||||
|
"This is usefull for non HTTP proxied services."
|
||||||
|
msgstr ""
|
||||||
|
"SLO 的 POST 请求使用的 URL. 空 = 服务地址\n"
|
||||||
|
"在为非 HTTP 代理服务时有用"
|
||||||
|
|
||||||
|
#: models.py:647
|
||||||
|
msgid "username allowed to connect to the service"
|
||||||
|
msgstr "允许连接到服务的用户名"
|
||||||
|
|
||||||
|
#: models.py:677
|
||||||
|
msgid "name of an attribute to send to the service, use * for all attributes"
|
||||||
|
msgstr "发给服务的属性名, 使用 * 表示所有属性"
|
||||||
|
|
||||||
|
#: models.py:684 models.py:765
|
||||||
|
msgid "replace"
|
||||||
|
msgstr "替换"
|
||||||
|
|
||||||
|
#: models.py:685
|
||||||
|
msgid ""
|
||||||
|
"name under which the attribute will be show to the service. empty = default "
|
||||||
|
"name of the attribut"
|
||||||
|
msgstr ""
|
||||||
|
"展示给服务的属性的名字. 空 = default"
|
||||||
|
"属性的名字"
|
||||||
|
|
||||||
|
#: models.py:716 models.py:751
|
||||||
|
msgid "attribute"
|
||||||
|
msgstr "属性"
|
||||||
|
|
||||||
|
#: models.py:717
|
||||||
|
msgid "Name of the attribute which must verify pattern"
|
||||||
|
msgstr "必须校验范式的属性的名字"
|
||||||
|
|
||||||
|
#: models.py:724
|
||||||
|
msgid "a regular expression"
|
||||||
|
msgstr "一个正则表达式"
|
||||||
|
|
||||||
|
#: models.py:752
|
||||||
|
msgid "Name of the attribute for which the value must be replace"
|
||||||
|
msgstr "必须被替换的值的属性的名字"
|
||||||
|
|
||||||
|
#: models.py:758
|
||||||
|
msgid "An regular expression maching whats need to be replaced"
|
||||||
|
msgstr "一个正则表达式,符合的将要被替换"
|
||||||
|
|
||||||
|
#: models.py:766
|
||||||
|
msgid "replace expression, groups are capture by \\1, \\2 …"
|
||||||
|
msgstr "替换表达式, 用 \\`, \\2 等等来替换组"
|
||||||
|
|
||||||
|
#: templates/cas_server/bs3/base.html:43 templates/cas_server/bs4/base.html:28
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"A new version of the application is available. This instance runs "
|
||||||
|
"%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
|
||||||
|
"upgrading."
|
||||||
|
msgstr ""
|
||||||
|
"此应用有一个新版本可用. 此实例运行于 %(VERSION)s, 最新的版本是 %(LAST_VERSION)s. 请考虑升级"
|
||||||
|
|
||||||
|
#: templates/cas_server/bs3/logged.html:4
|
||||||
|
#: templates/cas_server/bs4/logged.html:4
|
||||||
|
msgid ""
|
||||||
|
"<h3>Log In Successful</h3>You have successfully logged into the Central "
|
||||||
|
"Authentication Service.<br/>For security reasons, please Log Out and Exit "
|
||||||
|
"your web browser when you are done accessing services that require "
|
||||||
|
"authentication!"
|
||||||
|
msgstr ""
|
||||||
|
"<h3>登入成功</h3>您已经成功登入认证中心."
|
||||||
|
"<br/>出于安全考虑, 当您用完需要认证的服务时,请您登出并退出您的浏览器!"
|
||||||
|
|
||||||
|
#: templates/cas_server/bs3/logged.html:8
|
||||||
|
#: templates/cas_server/bs4/logged.html:8
|
||||||
|
msgid "Log me out from all my sessions"
|
||||||
|
msgstr "从我的所有会话中登出"
|
||||||
|
|
||||||
|
#: templates/cas_server/bs3/logged.html:14
|
||||||
|
#: templates/cas_server/bs4/logged.html:14
|
||||||
|
msgid "Forget the identity provider"
|
||||||
|
msgstr "忘掉身份提供者"
|
||||||
|
|
||||||
|
#: templates/cas_server/bs3/logged.html:18
|
||||||
|
#: templates/cas_server/bs4/logged.html:18
|
||||||
|
msgid "Logout"
|
||||||
|
msgstr "登出"
|
||||||
|
|
||||||
|
#: templates/cas_server/bs3/login.html:6 templates/cas_server/bs4/login.html:7
|
||||||
|
msgid "Please log in"
|
||||||
|
msgstr "请登录"
|
||||||
|
|
||||||
|
#: templates/cas_server/bs3/login.html:14
|
||||||
|
#: templates/cas_server/bs4/login.html:17
|
||||||
|
msgid "Login"
|
||||||
|
msgstr "登录"
|
||||||
|
|
||||||
|
#: templates/cas_server/bs3/warn.html:9 templates/cas_server/bs4/warn.html:9
|
||||||
|
msgid "Connect to the service"
|
||||||
|
msgstr "连接到服务"
|
||||||
|
|
||||||
|
#: utils.py:753
|
||||||
|
#, python-format
|
||||||
|
msgid "\"%(value)s\" is not a valid regular expression"
|
||||||
|
msgstr "\"%(value)s\" 不是一个有效的正则表达式"
|
||||||
|
|
||||||
|
#: views.py:197
|
||||||
|
msgid ""
|
||||||
|
"<h3>Logout successful</h3>You have successfully logged out from the Central "
|
||||||
|
"Authentication Service. For security reasons, close your web browser."
|
||||||
|
msgstr ""
|
||||||
|
"<h3>登出成功</h3>您成功从认证中心登出."
|
||||||
|
"安全起见,请关闭您的浏览器"
|
||||||
|
|
||||||
|
|
||||||
|
#: views.py:203
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"<h3>Logout successful</h3>You have successfully logged out from %d sessions "
|
||||||
|
"of the Central Authentication Service. For security reasons, close your web "
|
||||||
|
"browser."
|
||||||
|
msgstr ""
|
||||||
|
"<h3>登出成功</h3>您已经从认证中心服务的会话 %d 中成功登出"
|
||||||
|
"为安全起见,请关闭您的浏览器"
|
||||||
|
|
||||||
|
#: views.py:210
|
||||||
|
msgid ""
|
||||||
|
"<h3>Logout successful</h3>You were already logged out from the Central "
|
||||||
|
"Authentication Service. For security reasons, close your web browser."
|
||||||
|
msgstr ""
|
||||||
|
"<h3>登出成功</h3>您已经从认证中心服务登出. "
|
||||||
|
"为安全起见,请关闭您的浏览器"
|
||||||
|
|
||||||
|
#: views.py:391
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Invalid response from your identity provider CAS upon ticket %(ticket)s "
|
||||||
|
"validation: %(error)r"
|
||||||
|
msgstr ""
|
||||||
|
"您的身份提供者 CAS 对凭证 %(ticket)s 返回了无效响应"
|
||||||
|
"校验: %(error)r"
|
||||||
|
|
||||||
|
#: views.py:513
|
||||||
|
msgid "Invalid login ticket, please try to log in again"
|
||||||
|
msgstr "无效登录凭证, 请尝试重新登录"
|
||||||
|
|
||||||
|
#: views.py:706
|
||||||
|
#, python-format
|
||||||
|
msgid "Authentication has been required by service %(name)s (%(url)s)"
|
||||||
|
msgstr "服务 %(name)s (%(url)s) 需要认证"
|
||||||
|
|
||||||
|
#: views.py:744
|
||||||
|
#, python-format
|
||||||
|
msgid "Service %(url)s not allowed."
|
||||||
|
msgstr "不允许的服务 %(url)s"
|
||||||
|
|
||||||
|
#: views.py:751
|
||||||
|
msgid "Username not allowed"
|
||||||
|
msgstr "不允许的用户名"
|
||||||
|
|
||||||
|
#: views.py:758
|
||||||
|
msgid "User characteristics not allowed"
|
||||||
|
msgstr "不允许的用户特征"
|
||||||
|
|
||||||
|
#: views.py:765
|
||||||
|
#, python-format
|
||||||
|
msgid "The attribute %(field)s is needed to use that service"
|
||||||
|
msgstr "使用那个服务需要属性 %(field)s"
|
||||||
|
|
||||||
|
#: views.py:857
|
||||||
|
#, python-format
|
||||||
|
msgid "Authentication renewal required by service %(name)s (%(url)s)."
|
||||||
|
msgstr "服务 %(name)s (%(url)s) 需要更新认证"
|
||||||
|
|
||||||
|
#: views.py:864
|
||||||
|
#, python-format
|
||||||
|
msgid "Authentication required by service %(name)s (%(url)s)."
|
||||||
|
msgstr "服务 %(name)s (%(url)s) 需要认证."
|
||||||
|
|
||||||
|
#: views.py:872
|
||||||
|
#, python-format
|
||||||
|
msgid "Service %s not allowed"
|
||||||
|
msgstr "不允许的服务 %s"
|
|
@ -90,6 +90,7 @@ TIME_ZONE = 'UTC'
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
|
if django.VERSION < (4, 0):
|
||||||
USE_L10N = True
|
USE_L10N = True
|
||||||
|
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#
|
#
|
||||||
# (c) 2016 Valentin Samir
|
# (c) 2016 Valentin Samir
|
||||||
"""Tests module for utils"""
|
"""Tests module for utils"""
|
||||||
|
import django
|
||||||
from django.test import TestCase, RequestFactory
|
from django.test import TestCase, RequestFactory
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
|
||||||
|
@ -173,6 +174,7 @@ class UtilsTestCase(TestCase):
|
||||||
utils.import_attr('cas_server.utils.toto')
|
utils.import_attr('cas_server.utils.toto')
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
utils.import_attr('toto')
|
utils.import_attr('toto')
|
||||||
|
if django.VERSION < (3, 2):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
utils.import_attr('cas_server.default_app_config'),
|
utils.import_attr('cas_server.default_app_config'),
|
||||||
'cas_server.apps.CasAppConfig'
|
'cas_server.apps.CasAppConfig'
|
||||||
|
|
|
@ -262,7 +262,7 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
# check that the service pattern registered on the ticket is the on we use for tests
|
# check that the service pattern registered on the ticket is the on we use for tests
|
||||||
self.assertEqual(ticket.service_pattern, self.service_pattern)
|
self.assertEqual(ticket.service_pattern, self.service_pattern)
|
||||||
|
|
||||||
def assert_service_ticket(self, client, response):
|
def assert_service_ticket(self, client, response, service="https://www.example.com"):
|
||||||
"""check that a ticket is well emited when requested on a allowed service"""
|
"""check that a ticket is well emited when requested on a allowed service"""
|
||||||
# On ticket emission, we should be redirected to the service url, setting the ticket
|
# On ticket emission, we should be redirected to the service url, setting the ticket
|
||||||
# GET parameter
|
# GET parameter
|
||||||
|
@ -270,7 +270,7 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
self.assertTrue(response.has_header('Location'))
|
self.assertTrue(response.has_header('Location'))
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
response['Location'].startswith(
|
response['Location'].startswith(
|
||||||
"https://www.example.com?ticket=%s-" % settings.CAS_SERVICE_TICKET_PREFIX
|
"%s?ticket=%s-" % (service, settings.CAS_SERVICE_TICKET_PREFIX)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# check that the value of the ticket GET parameter match the value of the ticket
|
# check that the value of the ticket GET parameter match the value of the ticket
|
||||||
|
@ -337,6 +337,19 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin):
|
||||||
self.assertFalse(b"Service https://www.example.net not allowed" in response.content)
|
self.assertFalse(b"Service https://www.example.net not allowed" in response.content)
|
||||||
|
|
||||||
def test_view_login_get_auth_allowed_service(self):
|
def test_view_login_get_auth_allowed_service(self):
|
||||||
|
"""
|
||||||
|
Request a ticket for an allowed service by an authenticated client containing
|
||||||
|
non ascii char in url
|
||||||
|
"""
|
||||||
|
# get a client that is already authenticated
|
||||||
|
client = get_auth_client()
|
||||||
|
# ask for a ticket for https://www.example.com
|
||||||
|
response = client.get("/login?service=https://www.example.com/é")
|
||||||
|
# as https://www.example.com/é is a valid service a ticket should be created and the
|
||||||
|
# user redirected to the service url
|
||||||
|
self.assert_service_ticket(client, response, service="https://www.example.com/%C3%A9")
|
||||||
|
|
||||||
|
def test_view_login_get_auth_allowed_service_non_ascii(self):
|
||||||
"""Request a ticket for an allowed service by an authenticated client"""
|
"""Request a ticket for an allowed service by an authenticated client"""
|
||||||
# get a client that is already authenticated
|
# get a client that is already authenticated
|
||||||
client = get_auth_client()
|
client = get_auth_client()
|
||||||
|
|
|
@ -249,15 +249,25 @@ def update_url(url, params):
|
||||||
:return: The URL with an updated querystring
|
:return: The URL with an updated querystring
|
||||||
:rtype: unicode
|
:rtype: unicode
|
||||||
"""
|
"""
|
||||||
if not isinstance(url, bytes):
|
def to_unicode(data):
|
||||||
url = url.encode('utf-8')
|
if isinstance(data, bytes):
|
||||||
for key, value in list(params.items()):
|
return data.decode('utf-8')
|
||||||
if not isinstance(key, bytes):
|
else:
|
||||||
del params[key]
|
return data
|
||||||
key = key.encode('utf-8')
|
|
||||||
if not isinstance(value, bytes):
|
def to_bytes(data):
|
||||||
value = value.encode('utf-8')
|
if not isinstance(data, bytes):
|
||||||
params[key] = value
|
return data.encode('utf-8')
|
||||||
|
else:
|
||||||
|
return data
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
url = to_unicode(url)
|
||||||
|
params = {to_unicode(key): to_unicode(value) for (key, value) in params.items()}
|
||||||
|
else:
|
||||||
|
url = to_bytes(url)
|
||||||
|
params = {to_bytes(key): to_bytes(value) for (key, value) in params.items()}
|
||||||
|
|
||||||
url_parts = list(urlparse(url))
|
url_parts = list(urlparse(url))
|
||||||
query = dict(parse_qsl(url_parts[4], keep_blank_values=True))
|
query = dict(parse_qsl(url_parts[4], keep_blank_values=True))
|
||||||
query.update(params)
|
query.update(params)
|
||||||
|
@ -265,10 +275,12 @@ def update_url(url, params):
|
||||||
query = list(query.items())
|
query = list(query.items())
|
||||||
query.sort()
|
query.sort()
|
||||||
url_query = urlencode(query)
|
url_query = urlencode(query)
|
||||||
if not isinstance(url_query, bytes): # pragma: no cover in python3 urlencode return an unicode
|
|
||||||
url_query = url_query.encode("utf-8")
|
|
||||||
url_parts[4] = url_query
|
url_parts[4] = url_query
|
||||||
return urlunparse(url_parts).decode('utf-8')
|
url = urlunparse(url_parts)
|
||||||
|
|
||||||
|
if isinstance(url, bytes):
|
||||||
|
url = url.decode('utf-8')
|
||||||
|
return url
|
||||||
|
|
||||||
|
|
||||||
def unpack_nested_exception(error):
|
def unpack_nested_exception(error):
|
||||||
|
|
|
@ -153,6 +153,16 @@ class LogoutView(View, LogoutMixin):
|
||||||
self.url = request.GET.get('url')
|
self.url = request.GET.get('url')
|
||||||
self.ajax = settings.CAS_ENABLE_AJAX_AUTH and 'HTTP_X_AJAX' in request.META
|
self.ajax = settings.CAS_ENABLE_AJAX_AUTH and 'HTTP_X_AJAX' in request.META
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def delete_cookies(response):
|
||||||
|
if settings.CAS_REMOVE_DJANGO_SESSION_COOKIE_ON_LOGOUT:
|
||||||
|
response.delete_cookie(settings.SESSION_COOKIE_NAME)
|
||||||
|
if settings.CAS_REMOVE_DJANGO_CSRF_COOKIE_ON_LOGOUT:
|
||||||
|
response.delete_cookie(settings.CSRF_COOKIE_NAME)
|
||||||
|
if settings.CAS_REMOVE_DJANGO_LANGUAGE_COOKIE_ON_LOGOUT:
|
||||||
|
response.delete_cookie(settings.LANGUAGE_COOKIE_NAME)
|
||||||
|
return response
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
method called on GET request on this view
|
method called on GET request on this view
|
||||||
|
@ -181,15 +191,15 @@ class LogoutView(View, LogoutMixin):
|
||||||
response = HttpResponseRedirect(utils.update_url(url, params))
|
response = HttpResponseRedirect(utils.update_url(url, params))
|
||||||
if request.GET.get("forget_provider"):
|
if request.GET.get("forget_provider"):
|
||||||
response.delete_cookie("remember_provider")
|
response.delete_cookie("remember_provider")
|
||||||
return response
|
return self.delete_cookies(response)
|
||||||
# if service is set, redirect to service after logout
|
# if service is set, redirect to service after logout
|
||||||
if self.service:
|
if self.service:
|
||||||
list(messages.get_messages(request)) # clean messages before leaving the django app
|
list(messages.get_messages(request)) # clean messages before leaving the django app
|
||||||
return HttpResponseRedirect(self.service)
|
return self.delete_cookies(HttpResponseRedirect(self.service))
|
||||||
# if service is not set but url is set, redirect to url after logout
|
# if service is not set but url is set, redirect to url after logout
|
||||||
elif self.url:
|
elif self.url:
|
||||||
list(messages.get_messages(request)) # clean messages before leaving the django app
|
list(messages.get_messages(request)) # clean messages before leaving the django app
|
||||||
return HttpResponseRedirect(self.url)
|
return self.delete_cookies(HttpResponseRedirect(self.url))
|
||||||
else:
|
else:
|
||||||
# build logout message depending of the number of sessions the user logs out
|
# build logout message depending of the number of sessions the user logs out
|
||||||
if session_nb == 1:
|
if session_nb == 1:
|
||||||
|
@ -224,19 +234,19 @@ class LogoutView(View, LogoutMixin):
|
||||||
'url': url,
|
'url': url,
|
||||||
'session_nb': session_nb
|
'session_nb': session_nb
|
||||||
}
|
}
|
||||||
return json_response(request, data)
|
return self.delete_cookies(json_response(request, data))
|
||||||
else:
|
else:
|
||||||
return redirect("cas_server:login")
|
return self.delete_cookies(redirect("cas_server:login"))
|
||||||
else:
|
else:
|
||||||
if self.ajax:
|
if self.ajax:
|
||||||
data = {'status': 'success', 'detail': 'logout', 'session_nb': session_nb}
|
data = {'status': 'success', 'detail': 'logout', 'session_nb': session_nb}
|
||||||
return json_response(request, data)
|
return self.delete_cookies(json_response(request, data))
|
||||||
else:
|
else:
|
||||||
return render(
|
return self.delete_cookies(render(
|
||||||
request,
|
request,
|
||||||
settings.CAS_LOGOUT_TEMPLATE,
|
settings.CAS_LOGOUT_TEMPLATE,
|
||||||
utils.context({'logout_msg': logout_msg})
|
utils.context({'logout_msg': logout_msg})
|
||||||
)
|
))
|
||||||
|
|
||||||
|
|
||||||
class FederateAuth(CsrfExemptView):
|
class FederateAuth(CsrfExemptView):
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
testpaths = cas_server/tests/
|
testpaths = cas_server/tests/
|
||||||
DJANGO_SETTINGS_MODULE = cas_server.tests.settings
|
DJANGO_SETTINGS_MODULE = cas_server.tests.settings
|
||||||
norecursedirs = .* build dist docs
|
norecursedirs = .* build dist docs
|
||||||
python_paths = .
|
pythonpath = .
|
||||||
|
|
|
@ -4,8 +4,7 @@ requests_futures>=0.9.5
|
||||||
lxml>=3.4
|
lxml>=3.4
|
||||||
six>=1.8
|
six>=1.8
|
||||||
tox>=1.8.1
|
tox>=1.8.1
|
||||||
pytest>=2.6.4
|
pytest>=7
|
||||||
pytest-django>=2.8.0
|
pytest-django>=2.8.0
|
||||||
pytest-pythonpath>=0.3
|
|
||||||
pytest-cov>=2.2.1
|
pytest-cov>=2.2.1
|
||||||
mock>=1
|
mock>=1
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Django >= 1.11,<3.3
|
Django >= 1.11,<4.2
|
||||||
setuptools>=5.5
|
setuptools>=5.5
|
||||||
requests>=2.4
|
requests>=2.4
|
||||||
requests_futures>=0.9.5
|
requests_futures>=0.9.5
|
||||||
|
|
5
setup.py
5
setup.py
|
@ -40,14 +40,13 @@ if __name__ == '__main__':
|
||||||
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
|
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
|
||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Programming Language :: Python :: 2',
|
|
||||||
'Programming Language :: Python :: 2.7',
|
|
||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 3',
|
||||||
'Programming Language :: Python :: 3.5',
|
'Programming Language :: Python :: 3.5',
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.6',
|
||||||
'Programming Language :: Python :: 3.7',
|
'Programming Language :: Python :: 3.7',
|
||||||
'Programming Language :: Python :: 3.8',
|
'Programming Language :: Python :: 3.8',
|
||||||
'Programming Language :: Python :: 3.9',
|
'Programming Language :: Python :: 3.9',
|
||||||
|
'Programming Language :: Python :: 3.10',
|
||||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||||
'Topic :: Internet :: WWW/HTTP',
|
'Topic :: Internet :: WWW/HTTP',
|
||||||
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
|
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
|
||||||
|
@ -62,7 +61,7 @@ if __name__ == '__main__':
|
||||||
},
|
},
|
||||||
keywords=['django', 'cas', 'cas3', 'server', 'sso', 'single sign-on', 'authentication', 'auth'],
|
keywords=['django', 'cas', 'cas3', 'server', 'sso', 'single sign-on', 'authentication', 'auth'],
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'Django >= 1.11,<3.3', 'requests >= 2.4', 'requests_futures >= 0.9.5',
|
'Django >= 1.11,<4.2', 'requests >= 2.4', 'requests_futures >= 0.9.5',
|
||||||
'lxml >= 3.4', 'six >= 1'
|
'lxml >= 3.4', 'six >= 1'
|
||||||
],
|
],
|
||||||
url="https://github.com/nitmir/django-cas-server",
|
url="https://github.com/nitmir/django-cas-server",
|
||||||
|
|
44
tox.ini
44
tox.ini
|
@ -2,11 +2,12 @@
|
||||||
envlist=
|
envlist=
|
||||||
flake8,
|
flake8,
|
||||||
check_rst,
|
check_rst,
|
||||||
py27-django111,
|
|
||||||
py3-django111,
|
py3-django111,
|
||||||
py3-django22,
|
py3-django22,
|
||||||
py3-django31,
|
py3-django31,
|
||||||
py3-django32,
|
py3-django32,
|
||||||
|
py3-django40,
|
||||||
|
py3-django41,
|
||||||
|
|
||||||
##################
|
##################
|
||||||
# generic config #
|
# generic config #
|
||||||
|
@ -117,6 +118,18 @@ deps =
|
||||||
Django>=3.2,<3.3
|
Django>=3.2,<3.3
|
||||||
{[base]deps}
|
{[base]deps}
|
||||||
|
|
||||||
|
[testenv:py3-django40]
|
||||||
|
basepython=python3
|
||||||
|
deps =
|
||||||
|
Django>=4.0,<4.1
|
||||||
|
{[base]deps}
|
||||||
|
|
||||||
|
[testenv:py3-django41]
|
||||||
|
basepython=python3
|
||||||
|
deps =
|
||||||
|
Django>=4.1,<4.2
|
||||||
|
{[base]deps}
|
||||||
|
|
||||||
#########################
|
#########################
|
||||||
# Debian strech support #
|
# Debian strech support #
|
||||||
#########################
|
#########################
|
||||||
|
@ -167,9 +180,9 @@ deps =
|
||||||
Django>=2.2,<3.0
|
Django>=2.2,<3.0
|
||||||
{[base]deps}
|
{[base]deps}
|
||||||
|
|
||||||
##############################################
|
#########################################################
|
||||||
# Debian bullseye and Ubuntu hirsute support #
|
# Debian bullseye and Ubuntu hirsute and impish support #
|
||||||
##############################################
|
#########################################################
|
||||||
|
|
||||||
[testenv:py39-django22]
|
[testenv:py39-django22]
|
||||||
basepython=python3.9
|
basepython=python3.9
|
||||||
|
@ -177,6 +190,17 @@ deps =
|
||||||
Django>=2.2,<3.0
|
Django>=2.2,<3.0
|
||||||
{[base]deps}
|
{[base]deps}
|
||||||
|
|
||||||
|
####################################
|
||||||
|
# Ubuntu jammy and kinetic support #
|
||||||
|
####################################
|
||||||
|
|
||||||
|
[testenv:py310-django32]
|
||||||
|
basepython=python3.10
|
||||||
|
deps =
|
||||||
|
Django>=3.2,<3.3
|
||||||
|
{[base]deps}
|
||||||
|
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Django additional supported version #
|
# Django additional supported version #
|
||||||
#######################################
|
#######################################
|
||||||
|
@ -193,3 +217,15 @@ basepython=python3.9
|
||||||
deps =
|
deps =
|
||||||
Django>=3.2,<3.3
|
Django>=3.2,<3.3
|
||||||
{[base]deps}
|
{[base]deps}
|
||||||
|
|
||||||
|
[testenv:py310-django40]
|
||||||
|
basepython=python3.10
|
||||||
|
deps =
|
||||||
|
Django>=4.0,<4.1
|
||||||
|
{[base]deps}
|
||||||
|
|
||||||
|
[testenv:py310-django41]
|
||||||
|
basepython=python3.10
|
||||||
|
deps =
|
||||||
|
Django>=4.1,<4.2
|
||||||
|
{[base]deps}
|
||||||
|
|
Loading…
Reference in a new issue