apply isort and pyupgrade (#55990)
This commit is contained in:
parent
2704f4feaa
commit
4729ef9a3b
|
@ -6,3 +6,13 @@ repos:
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
args: ['--target-version', 'py37', '--skip-string-normalization', '--line-length', '110']
|
args: ['--target-version', 'py37', '--skip-string-normalization', '--line-length', '110']
|
||||||
|
- repo: https://github.com/PyCQA/isort
|
||||||
|
rev: 5.7.0
|
||||||
|
hooks:
|
||||||
|
- id: isort
|
||||||
|
args: ['--profile', 'black', '--line-length', '110']
|
||||||
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
|
rev: v2.20.0
|
||||||
|
hooks:
|
||||||
|
- id: pyupgrade
|
||||||
|
args: ['--keep-percent-format', '--py37-plus']
|
||||||
|
|
14
README
14
README
|
@ -319,7 +319,7 @@ Unit tests are written using pytest and launched using tox, and can be run with:
|
||||||
tox
|
tox
|
||||||
|
|
||||||
Code Style
|
Code Style
|
||||||
----------
|
==========
|
||||||
|
|
||||||
black is used to format the code, using thoses parameters:
|
black is used to format the code, using thoses parameters:
|
||||||
|
|
||||||
|
@ -328,6 +328,18 @@ black is used to format the code, using thoses parameters:
|
||||||
There is .pre-commit-config.yaml to use pre-commit to automatically run black
|
There is .pre-commit-config.yaml to use pre-commit to automatically run black
|
||||||
before commits. (execute `pre-commit install` to install the git hook.)
|
before commits. (execute `pre-commit install` to install the git hook.)
|
||||||
|
|
||||||
|
isort is used to format the imports, using those parameter:
|
||||||
|
|
||||||
|
isort --profile black --line-length 110
|
||||||
|
|
||||||
|
pyupgrade is used to automatically upgrade syntax, using those parameters:
|
||||||
|
|
||||||
|
pyupgrade --keep-percent-format --py37-plus
|
||||||
|
|
||||||
|
There is .pre-commit-config.yaml to use pre-commit to automatically run black,
|
||||||
|
isort and pyupgrade before commits. (execute `pre-commit install` to install
|
||||||
|
the git hook.)
|
||||||
|
|
||||||
Remarks
|
Remarks
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
|
|
@ -13,33 +13,29 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from xml.etree import ElementTree as ET
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
import lasso
|
import lasso
|
||||||
import requests
|
import requests
|
||||||
import requests.exceptions
|
import requests.exceptions
|
||||||
from atomicwrites import atomic_write
|
from atomicwrites import atomic_write
|
||||||
|
from django.contrib import auth, messages
|
||||||
from django.core.exceptions import PermissionDenied, FieldDoesNotExist
|
|
||||||
from django.core.files.storage import default_storage
|
|
||||||
from django.contrib import auth
|
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.contrib import messages
|
from django.core.exceptions import FieldDoesNotExist, PermissionDenied
|
||||||
|
from django.core.files.storage import default_storage
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.six.moves.urllib.parse import urlparse
|
from django.utils.six.moves.urllib.parse import urlparse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from . import utils, app_settings, models
|
from . import app_settings, models, utils
|
||||||
|
|
||||||
User = auth.get_user_model()
|
User = auth.get_user_model()
|
||||||
|
|
||||||
|
@ -51,7 +47,7 @@ class UserCreationError(Exception):
|
||||||
|
|
||||||
|
|
||||||
def display_truncated_list(l, max_length=10):
|
def display_truncated_list(l, max_length=10):
|
||||||
s = '[' + ', '.join(map(six.text_type, l))
|
s = '[' + ', '.join(map(str, l))
|
||||||
if len(l) > max_length:
|
if len(l) > max_length:
|
||||||
s += '..truncated more than %d items (%d)]' % (max_length, len(l))
|
s += '..truncated more than %d items (%d)]' % (max_length, len(l))
|
||||||
else:
|
else:
|
||||||
|
@ -59,7 +55,7 @@ def display_truncated_list(l, max_length=10):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
class DefaultAdapter(object):
|
class DefaultAdapter:
|
||||||
def __init__(self, request=None):
|
def __init__(self, request=None):
|
||||||
self.request = request
|
self.request = request
|
||||||
|
|
||||||
|
@ -153,7 +149,7 @@ class DefaultAdapter(object):
|
||||||
idp['METADATA'] = fd.read()
|
idp['METADATA'] = fd.read()
|
||||||
# use file cache mtime as last_update time, prevent too many loading from different workers
|
# use file cache mtime as last_update time, prevent too many loading from different workers
|
||||||
last_update = max(last_update, os.stat(file_cache_path).st_mtime)
|
last_update = max(last_update, os.stat(file_cache_path).st_mtime)
|
||||||
except (IOError, OSError):
|
except OSError:
|
||||||
warning('metadata url %s : error when loading the file cache %s', url, file_cache_path)
|
warning('metadata url %s : error when loading the file cache %s', url, file_cache_path)
|
||||||
|
|
||||||
# fresh cache, skip loading
|
# fresh cache, skip loading
|
||||||
|
@ -305,7 +301,7 @@ class DefaultAdapter(object):
|
||||||
if saml_attributes['name_id_format'] == lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT:
|
if saml_attributes['name_id_format'] == lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT:
|
||||||
if transient_federation_attribute and saml_attributes.get(transient_federation_attribute):
|
if transient_federation_attribute and saml_attributes.get(transient_federation_attribute):
|
||||||
name_id = saml_attributes[transient_federation_attribute]
|
name_id = saml_attributes[transient_federation_attribute]
|
||||||
if not isinstance(name_id, six.string_types):
|
if not isinstance(name_id, str):
|
||||||
if len(name_id) == 1:
|
if len(name_id) == 1:
|
||||||
name_id = name_id[0]
|
name_id = name_id[0]
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
class AppSettings(object):
|
class AppSettings:
|
||||||
__PREFIX = 'MELLON_'
|
__PREFIX = 'MELLON_'
|
||||||
__DEFAULTS = {
|
__DEFAULTS = {
|
||||||
'IDENTITY_PROVIDERS': [],
|
'IDENTITY_PROVIDERS': [],
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.contrib.auth.backends import ModelBackend
|
from django.contrib.auth.backends import ModelBackend
|
||||||
|
|
|
@ -13,12 +13,11 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.utils.http import urlencode
|
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.utils.deprecation import MiddlewareMixin
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.deprecation import MiddlewareMixin
|
||||||
|
from django.utils.http import urlencode
|
||||||
|
|
||||||
from . import app_settings, utils
|
from . import app_settings, utils
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
@ -40,6 +37,6 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='usersamlidentifier',
|
name='usersamlidentifier',
|
||||||
unique_together=set([('issuer', 'name_id')]),
|
unique_together={('issuer', 'name_id')},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Generated by Django 2.2.12 on 2020-04-24 05:14
|
# Generated by Django 2.2.12 on 2020-04-24 05:14
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
|
@ -13,13 +13,12 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
|
|
||||||
class UserSAMLIdentifier(models.Model):
|
class UserSAMLIdentifier(models.Model):
|
||||||
|
|
|
@ -29,10 +29,10 @@ class SessionStore(SessionStore):
|
||||||
session_not_on_or_after = self.get_session_not_on_or_after()
|
session_not_on_or_after = self.get_session_not_on_or_after()
|
||||||
if session_not_on_or_after and 'expiry' not in kwargs:
|
if session_not_on_or_after and 'expiry' not in kwargs:
|
||||||
kwargs['expiry'] = session_not_on_or_after
|
kwargs['expiry'] = session_not_on_or_after
|
||||||
return super(SessionStore, self).get_expiry_age(**kwargs)
|
return super().get_expiry_age(**kwargs)
|
||||||
|
|
||||||
def get_expiry_date(self, **kwargs):
|
def get_expiry_date(self, **kwargs):
|
||||||
session_not_on_or_after = self.get_session_not_on_or_after()
|
session_not_on_or_after = self.get_session_not_on_or_after()
|
||||||
if session_not_on_or_after and 'expiry' not in kwargs:
|
if session_not_on_or_after and 'expiry' not in kwargs:
|
||||||
kwargs['expiry'] = session_not_on_or_after
|
kwargs['expiry'] = session_not_on_or_after
|
||||||
return super(SessionStore, self).get_expiry_date(**kwargs)
|
return super().get_expiry_date(**kwargs)
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf.urls import url
|
|
||||||
import django
|
import django
|
||||||
|
from django.conf.urls import url
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url('login/$', views.login, name='mellon_login'),
|
url('login/$', views.login, name='mellon_login'),
|
||||||
url('login/debug/$', views.debug_login, name='mellon_debug_login'),
|
url('login/debug/$', views.debug_login, name='mellon_debug_login'),
|
||||||
|
|
|
@ -13,23 +13,22 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import datetime
|
import datetime
|
||||||
import importlib
|
import importlib
|
||||||
|
import logging
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
import isodate
|
|
||||||
from xml.parsers import expat
|
from xml.parsers import expat
|
||||||
|
|
||||||
|
import isodate
|
||||||
|
import lasso
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.timezone import make_aware, now, make_naive, is_aware, get_default_timezone
|
|
||||||
from django.conf import settings
|
|
||||||
from django.utils.six.moves.urllib.parse import urlparse
|
from django.utils.six.moves.urllib.parse import urlparse
|
||||||
import lasso
|
from django.utils.timezone import get_default_timezone, is_aware, make_aware, make_naive, now
|
||||||
|
|
||||||
from . import app_settings
|
from . import app_settings
|
||||||
|
|
||||||
|
@ -133,8 +132,7 @@ def get_idp(entity_id):
|
||||||
def get_idps():
|
def get_idps():
|
||||||
for adapter in get_adapters():
|
for adapter in get_adapters():
|
||||||
if hasattr(adapter, 'get_idps'):
|
if hasattr(adapter, 'get_idps'):
|
||||||
for idp in adapter.get_idps():
|
yield from adapter.get_idps()
|
||||||
yield idp
|
|
||||||
|
|
||||||
|
|
||||||
def flatten_datetime(d):
|
def flatten_datetime(d):
|
||||||
|
|
|
@ -13,53 +13,43 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import uuid
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
from contextlib import contextmanager, nullcontext
|
from contextlib import contextmanager, nullcontext
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
import logging
|
|
||||||
import requests
|
|
||||||
import lasso
|
|
||||||
import uuid
|
|
||||||
from requests.exceptions import RequestException
|
|
||||||
from xml.sax.saxutils import escape
|
from xml.sax.saxutils import escape
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
|
|
||||||
import django.http
|
import django.http
|
||||||
from django.views.generic import View
|
import lasso
|
||||||
from django.views.generic.base import RedirectView
|
import requests
|
||||||
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden
|
|
||||||
from django.contrib import auth
|
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.contrib import auth
|
||||||
|
from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model
|
||||||
|
from django.db import transaction
|
||||||
|
from django.http import HttpResponse, HttpResponseForbidden, HttpResponseRedirect
|
||||||
from django.shortcuts import render, resolve_url
|
from django.shortcuts import render, resolve_url
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.http import urlencode
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.encoding import force_text, force_str
|
from django.utils.encoding import force_str, force_text
|
||||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
from django.utils.http import urlencode
|
||||||
from django.db import transaction
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from django.views.generic import View
|
||||||
|
from django.views.generic.base import RedirectView
|
||||||
|
from requests.exceptions import RequestException
|
||||||
|
|
||||||
from . import app_settings, utils, models
|
from . import app_settings, models, utils
|
||||||
|
|
||||||
|
|
||||||
RETRY_LOGIN_COOKIE = 'MELLON_RETRY_LOGIN'
|
RETRY_LOGIN_COOKIE = 'MELLON_RETRY_LOGIN'
|
||||||
|
|
||||||
lasso.setFlag('thin-sessions')
|
lasso.setFlag('thin-sessions')
|
||||||
|
|
||||||
if six.PY3:
|
|
||||||
|
|
||||||
def lasso_decode(x):
|
def lasso_decode(x):
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
def lasso_decode(x):
|
|
||||||
return x.decode('utf-8')
|
|
||||||
|
|
||||||
|
|
||||||
EO_NS = 'https://www.entrouvert.com/'
|
EO_NS = 'https://www.entrouvert.com/'
|
||||||
|
@ -71,16 +61,16 @@ User = get_user_model()
|
||||||
class HttpResponseBadRequest(django.http.HttpResponseBadRequest):
|
class HttpResponseBadRequest(django.http.HttpResponseBadRequest):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
kwargs['content_type'] = kwargs.get('content_type', 'text/plain')
|
kwargs['content_type'] = kwargs.get('content_type', 'text/plain')
|
||||||
super(HttpResponseBadRequest, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self['X-Content-Type-Options'] = 'nosniff'
|
self['X-Content-Type-Options'] = 'nosniff'
|
||||||
|
|
||||||
|
|
||||||
class LogMixin(object):
|
class LogMixin:
|
||||||
"""Initialize a module logger in new objects"""
|
"""Initialize a module logger in new objects"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.log = logging.getLogger(__name__)
|
self.log = logging.getLogger(__name__)
|
||||||
super(LogMixin, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def check_next_url(request, next_url):
|
def check_next_url(request, next_url):
|
||||||
|
@ -101,7 +91,7 @@ def check_next_url(request, next_url):
|
||||||
return next_url
|
return next_url
|
||||||
|
|
||||||
|
|
||||||
class ProfileMixin(object):
|
class ProfileMixin:
|
||||||
profile = None
|
profile = None
|
||||||
|
|
||||||
def set_next_url(self, next_url):
|
def set_next_url(self, next_url):
|
||||||
|
@ -507,7 +497,7 @@ class LoginView(ProfileMixin, LogMixin, View):
|
||||||
# configure requested AuthnClassRef
|
# configure requested AuthnClassRef
|
||||||
authn_classref = utils.get_setting(idp, 'AUTHN_CLASSREF')
|
authn_classref = utils.get_setting(idp, 'AUTHN_CLASSREF')
|
||||||
if authn_classref:
|
if authn_classref:
|
||||||
authn_classref = tuple([str(x) for x in authn_classref])
|
authn_classref = tuple(str(x) for x in authn_classref)
|
||||||
req_authncontext = lasso.Samlp2RequestedAuthnContext()
|
req_authncontext = lasso.Samlp2RequestedAuthnContext()
|
||||||
authn_request.requestedAuthnContext = req_authncontext
|
authn_request.requestedAuthnContext = req_authncontext
|
||||||
req_authncontext.authnContextClassRef = authn_classref
|
req_authncontext.authnContextClassRef = authn_classref
|
||||||
|
@ -550,8 +540,7 @@ class LoginView(ProfileMixin, LogMixin, View):
|
||||||
assert hasattr(authn_request.extensions, 'any'), 'extension nodes need lasso > 2.5.1'
|
assert hasattr(authn_request.extensions, 'any'), 'extension nodes need lasso > 2.5.1'
|
||||||
serialized = ET.tostring(node, 'utf-8')
|
serialized = ET.tostring(node, 'utf-8')
|
||||||
# tostring return bytes in PY3, but lasso needs str
|
# tostring return bytes in PY3, but lasso needs str
|
||||||
if six.PY3:
|
serialized = serialized.decode('utf-8')
|
||||||
serialized = serialized.decode('utf-8')
|
|
||||||
extension_content = authn_request.extensions.any or ()
|
extension_content = authn_request.extensions.any or ()
|
||||||
extension_content += (serialized,)
|
extension_content += (serialized,)
|
||||||
authn_request.extensions.any = extension_content
|
authn_request.extensions.any = extension_content
|
||||||
|
@ -653,7 +642,7 @@ class LogoutView(ProfileMixin, LogMixin, View):
|
||||||
return HttpResponseBadRequest('error processing logout request: %r' % e)
|
return HttpResponseBadRequest('error processing logout request: %r' % e)
|
||||||
|
|
||||||
issuer = force_text(logout.remoteProviderId)
|
issuer = force_text(logout.remoteProviderId)
|
||||||
session_indexes = set(force_text(sessionIndex) for sessionIndex in logout.request.sessionIndexes)
|
session_indexes = {force_text(sessionIndex) for sessionIndex in logout.request.sessionIndexes}
|
||||||
|
|
||||||
saml_identifier = (
|
saml_identifier = (
|
||||||
models.UserSAMLIdentifier.objects.filter(
|
models.UserSAMLIdentifier.objects.filter(
|
||||||
|
|
12
setup.py
12
setup.py
|
@ -5,11 +5,12 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
from setuptools import setup, find_packages
|
|
||||||
from setuptools.command.install_lib import install_lib as _install_lib
|
|
||||||
from distutils.command.build import build as _build
|
|
||||||
from setuptools.command.sdist import sdist as _sdist
|
|
||||||
from distutils.cmd import Command
|
from distutils.cmd import Command
|
||||||
|
from distutils.command.build import build as _build
|
||||||
|
|
||||||
|
from setuptools import find_packages, setup
|
||||||
|
from setuptools.command.install_lib import install_lib as _install_lib
|
||||||
|
from setuptools.command.sdist import sdist as _sdist
|
||||||
|
|
||||||
|
|
||||||
class compile_translations(Command):
|
class compile_translations(Command):
|
||||||
|
@ -24,6 +25,7 @@ class compile_translations(Command):
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
|
|
||||||
os.environ.pop('DJANGO_SETTINGS_MODULE', None)
|
os.environ.pop('DJANGO_SETTINGS_MODULE', None)
|
||||||
|
@ -68,7 +70,7 @@ def get_version():
|
||||||
tag exists, take 0.0.0- and add the length of the commit log.
|
tag exists, take 0.0.0- and add the length of the commit log.
|
||||||
"""
|
"""
|
||||||
if os.path.exists('VERSION'):
|
if os.path.exists('VERSION'):
|
||||||
with open('VERSION', 'r') as v:
|
with open('VERSION') as v:
|
||||||
return v.read()
|
return v.read()
|
||||||
if os.path.exists('.git'):
|
if os.path.exists('.git'):
|
||||||
p = subprocess.Popen(
|
p = subprocess.Popen(
|
||||||
|
|
|
@ -13,11 +13,11 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
import pytest
|
|
||||||
import django_webtest
|
import django_webtest
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
|
|
|
@ -13,24 +13,21 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
import lasso
|
|
||||||
import time
|
import time
|
||||||
from multiprocessing.pool import ThreadPool
|
from multiprocessing.pool import ThreadPool
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
import mock
|
import lasso
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
|
||||||
from mellon.adapters import DefaultAdapter
|
from mellon.adapters import DefaultAdapter
|
||||||
from mellon.backends import SAMLBackend
|
from mellon.backends import SAMLBackend
|
||||||
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
User = auth.get_user_model()
|
User = auth.get_user_model()
|
||||||
|
@ -119,7 +116,7 @@ def test_lookup_user_transaction(transactional_db, concurrency, idp, saml_attrib
|
||||||
users = p.map(f, range(concurrency))
|
users = p.map(f, range(concurrency))
|
||||||
|
|
||||||
assert len(users) == concurrency
|
assert len(users) == concurrency
|
||||||
assert len(set(user.pk for user in users)) == 1
|
assert len({user.pk for user in users}) == 1
|
||||||
|
|
||||||
|
|
||||||
def test_provision_user_attributes(settings, django_user_model, idp, saml_attributes, caplog):
|
def test_provision_user_attributes(settings, django_user_model, idp, saml_attributes, caplog):
|
||||||
|
|
|
@ -13,32 +13,29 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
from html import unescape
|
|
||||||
import re
|
|
||||||
import base64
|
import base64
|
||||||
import zlib
|
import datetime
|
||||||
|
import re
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
import zlib
|
||||||
|
from html import unescape
|
||||||
|
|
||||||
import lasso
|
import lasso
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pytest import fixture
|
|
||||||
|
|
||||||
from django.contrib.sessions.models import Session
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib.sessions.models import Session
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.six.moves.urllib import parse as urlparse
|
|
||||||
from django.utils.encoding import force_str
|
from django.utils.encoding import force_str
|
||||||
|
from django.utils.six.moves.urllib import parse as urlparse
|
||||||
|
from httmock import HTTMock, all_requests
|
||||||
|
from httmock import response as mock_response
|
||||||
|
from pytest import fixture
|
||||||
|
|
||||||
from mellon.utils import create_metadata
|
from mellon.utils import create_metadata
|
||||||
from mellon.views import lasso_decode
|
from mellon.views import lasso_decode
|
||||||
|
|
||||||
from httmock import all_requests, HTTMock, response as mock_response
|
|
||||||
|
|
||||||
|
|
||||||
@fixture
|
@fixture
|
||||||
def idp_metadata():
|
def idp_metadata():
|
||||||
|
@ -81,7 +78,7 @@ def sp_metadata(sp_settings, rf):
|
||||||
return create_metadata(request)
|
return create_metadata(request)
|
||||||
|
|
||||||
|
|
||||||
class MockIdp(object):
|
class MockIdp:
|
||||||
session_dump = None
|
session_dump = None
|
||||||
identity_dump = None
|
identity_dump = None
|
||||||
|
|
||||||
|
@ -240,10 +237,10 @@ def test_sso_slo(db, app, idp, caplog, sp_settings):
|
||||||
assert 'created new user' in caplog.text
|
assert 'created new user' in caplog.text
|
||||||
assert 'logged in using SAML' in caplog.text
|
assert 'logged in using SAML' in caplog.text
|
||||||
assert urlparse.urlparse(response['Location']).path == '/whatever/'
|
assert urlparse.urlparse(response['Location']).path == '/whatever/'
|
||||||
response = app.get(reverse('mellon_logout'), extra_environ={'HTTP_REFERER': str('/some/path')})
|
response = app.get(reverse('mellon_logout'), extra_environ={'HTTP_REFERER': '/some/path'})
|
||||||
assert urlparse.urlparse(response['Location']).path == '/singleLogout'
|
assert urlparse.urlparse(response['Location']).path == '/singleLogout'
|
||||||
# again, user is already logged out
|
# again, user is already logged out
|
||||||
response = app.get(reverse('mellon_logout'), extra_environ={'HTTP_REFERER': str('/some/path')})
|
response = app.get(reverse('mellon_logout'), extra_environ={'HTTP_REFERER': '/some/path'})
|
||||||
assert urlparse.urlparse(response['Location']).path == '/some/path'
|
assert urlparse.urlparse(response['Location']).path == '/some/path'
|
||||||
|
|
||||||
|
|
||||||
|
@ -433,18 +430,11 @@ def test_sso_request_denied(db, app, idp, caplog, sp_settings):
|
||||||
assert not relay_state
|
assert not relay_state
|
||||||
assert url.endswith(reverse('mellon_login'))
|
assert url.endswith(reverse('mellon_login'))
|
||||||
response = app.post(reverse('mellon_login'), params={'SAMLResponse': body, 'RelayState': relay_state})
|
response = app.post(reverse('mellon_login'), params={'SAMLResponse': body, 'RelayState': relay_state})
|
||||||
if six.PY3:
|
assert (
|
||||||
assert (
|
"status is not success codes: ['urn:oasis:names:tc:SAML:2.0:status:Responder',\
|
||||||
"status is not success codes: ['urn:oasis:names:tc:SAML:2.0:status:Responder',\
|
|
||||||
'urn:oasis:names:tc:SAML:2.0:status:RequestDenied']"
|
'urn:oasis:names:tc:SAML:2.0:status:RequestDenied']"
|
||||||
in caplog.text
|
in caplog.text
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
assert (
|
|
||||||
"status is not success codes: [u'urn:oasis:names:tc:SAML:2.0:status:Responder',\
|
|
||||||
u'urn:oasis:names:tc:SAML:2.0:status:RequestDenied']"
|
|
||||||
in caplog.text
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.urls('urls_tests_template_base')
|
@pytest.mark.urls('urls_tests_template_base')
|
||||||
|
@ -663,7 +653,7 @@ def test_passive_auth_middleware_ok(db, app, idp, caplog, settings):
|
||||||
settings.MELLON_OPENED_SESSION_COOKIE_NAME = 'IDP_SESSION'
|
settings.MELLON_OPENED_SESSION_COOKIE_NAME = 'IDP_SESSION'
|
||||||
assert 'MELLON_PASSIVE_TRIED' not in app.cookies
|
assert 'MELLON_PASSIVE_TRIED' not in app.cookies
|
||||||
# webtest-lint is against unicode
|
# webtest-lint is against unicode
|
||||||
app.set_cookie(str('IDP_SESSION'), str('1'))
|
app.set_cookie('IDP_SESSION', '1')
|
||||||
response = app.get('/', headers={'Accept': force_str('text/html')}, status=302)
|
response = app.get('/', headers={'Accept': force_str('text/html')}, status=302)
|
||||||
assert urlparse.urlparse(response.location).path == '/login/'
|
assert urlparse.urlparse(response.location).path == '/login/'
|
||||||
assert urlparse.parse_qs(urlparse.urlparse(response.location).query, keep_blank_values=True) == {
|
assert urlparse.parse_qs(urlparse.urlparse(response.location).query, keep_blank_values=True) == {
|
||||||
|
@ -681,7 +671,7 @@ def test_passive_auth_middleware_ok(db, app, idp, caplog, settings):
|
||||||
assert 'MELLON_PASSIVE_TRIED' not in app.cookies
|
assert 'MELLON_PASSIVE_TRIED' not in app.cookies
|
||||||
|
|
||||||
# check passive authentication is tried again
|
# check passive authentication is tried again
|
||||||
app.set_cookie(str('IDP_SESSION'), str('1'))
|
app.set_cookie('IDP_SESSION', '1')
|
||||||
response = app.get('/', headers={'Accept': force_str('text/html')}, status=302)
|
response = app.get('/', headers={'Accept': force_str('text/html')}, status=302)
|
||||||
assert urlparse.urlparse(response.location).path == '/login/'
|
assert urlparse.urlparse(response.location).path == '/login/'
|
||||||
assert urlparse.parse_qs(urlparse.urlparse(response.location).query, keep_blank_values=True) == {
|
assert urlparse.parse_qs(urlparse.urlparse(response.location).query, keep_blank_values=True) == {
|
||||||
|
@ -695,7 +685,7 @@ def test_passive_auth_middleware_no_passive_auth_parameter(db, app, idp, caplog,
|
||||||
settings.MELLON_OPENED_SESSION_COOKIE_NAME = 'IDP_SESSION'
|
settings.MELLON_OPENED_SESSION_COOKIE_NAME = 'IDP_SESSION'
|
||||||
assert 'MELLON_PASSIVE_TRIED' not in app.cookies
|
assert 'MELLON_PASSIVE_TRIED' not in app.cookies
|
||||||
# webtest-lint is against unicode
|
# webtest-lint is against unicode
|
||||||
app.set_cookie(str('IDP_SESSION'), str('1'))
|
app.set_cookie('IDP_SESSION', '1')
|
||||||
app.get('/?no-passive-auth', headers={'Accept': force_str('text/html')}, status=200)
|
app.get('/?no-passive-auth', headers={'Accept': force_str('text/html')}, status=200)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,17 +13,16 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
import mock
|
|
||||||
import lasso
|
import lasso
|
||||||
|
|
||||||
from mellon.utils import create_metadata, iso8601_to_datetime, flatten_datetime
|
|
||||||
from mellon.views import check_next_url
|
|
||||||
from xml_utils import assert_xml_constraints
|
from xml_utils import assert_xml_constraints
|
||||||
|
|
||||||
|
from mellon.utils import create_metadata, flatten_datetime, iso8601_to_datetime
|
||||||
|
from mellon.views import check_next_url
|
||||||
|
|
||||||
|
|
||||||
def test_create_metadata(rf, private_settings, caplog):
|
def test_create_metadata(rf, private_settings, caplog):
|
||||||
ns = {
|
ns = {
|
||||||
|
@ -144,17 +143,17 @@ def test_flatten_datetime():
|
||||||
'y': 1,
|
'y': 1,
|
||||||
'z': 'u',
|
'z': 'u',
|
||||||
}
|
}
|
||||||
assert set(flatten_datetime(d).keys()) == set(['x', 'y', 'z'])
|
assert set(flatten_datetime(d).keys()) == {'x', 'y', 'z'}
|
||||||
assert flatten_datetime(d)['x'] == '2010-10-10T10:10:34'
|
assert flatten_datetime(d)['x'] == '2010-10-10T10:10:34'
|
||||||
assert flatten_datetime(d)['y'] == 1
|
assert flatten_datetime(d)['y'] == 1
|
||||||
assert flatten_datetime(d)['z'] == 'u'
|
assert flatten_datetime(d)['z'] == 'u'
|
||||||
|
|
||||||
|
|
||||||
def test_check_next_url(rf):
|
def test_check_next_url(rf):
|
||||||
assert not check_next_url(rf.get('/'), u'')
|
assert not check_next_url(rf.get('/'), '')
|
||||||
assert not check_next_url(rf.get('/'), None)
|
assert not check_next_url(rf.get('/'), None)
|
||||||
assert not check_next_url(rf.get('/'), u'\x00')
|
assert not check_next_url(rf.get('/'), '\x00')
|
||||||
assert not check_next_url(rf.get('/'), u'\u010e')
|
assert not check_next_url(rf.get('/'), '\u010e')
|
||||||
assert not check_next_url(rf.get('/'), u'https://example.invalid/')
|
assert not check_next_url(rf.get('/'), 'https://example.invalid/')
|
||||||
# default hostname is testserver
|
# default hostname is testserver
|
||||||
assert check_next_url(rf.get('/'), u'http://testserver/ok/')
|
assert check_next_url(rf.get('/'), 'http://testserver/ok/')
|
||||||
|
|
|
@ -13,23 +13,20 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
import mock
|
|
||||||
import lasso
|
|
||||||
from django.utils.six.moves.urllib.parse import parse_qs, urlparse
|
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
from httmock import HTTMock
|
from unittest import mock
|
||||||
|
|
||||||
|
import lasso
|
||||||
|
import pytest
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.http import urlencode
|
from django.utils.http import urlencode
|
||||||
|
from django.utils.six.moves.urllib.parse import parse_qs, urlparse
|
||||||
from xml_utils import assert_xml_constraints
|
from httmock import HTTMock
|
||||||
|
|
||||||
from utils import error_500, html_response
|
from utils import error_500, html_response
|
||||||
|
from xml_utils import assert_xml_constraints
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
@ -207,7 +204,7 @@ def test_sp_initiated_login(private_settings, client):
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
params = parse_qs(urlparse(response['Location']).query)
|
params = parse_qs(urlparse(response['Location']).query)
|
||||||
assert response['Location'].startswith('http://idp5/singleSignOn?')
|
assert response['Location'].startswith('http://idp5/singleSignOn?')
|
||||||
assert set(params.keys()) == set(['SAMLRequest', 'RelayState'])
|
assert set(params.keys()) == {'SAMLRequest', 'RelayState'}
|
||||||
assert len(params['SAMLRequest']) == 1
|
assert len(params['SAMLRequest']) == 1
|
||||||
assert base64.b64decode(params['SAMLRequest'][0])
|
assert base64.b64decode(params['SAMLRequest'][0])
|
||||||
assert client.session['mellon_next_url_%s' % params['RelayState'][0]] == '/whatever'
|
assert client.session['mellon_next_url_%s' % params['RelayState'][0]] == '/whatever'
|
||||||
|
@ -229,7 +226,7 @@ def test_sp_initiated_login_chosen(private_settings, client):
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
params = parse_qs(urlparse(response['Location']).query)
|
params = parse_qs(urlparse(response['Location']).query)
|
||||||
assert response['Location'].startswith('http://idp5/singleSignOn?')
|
assert response['Location'].startswith('http://idp5/singleSignOn?')
|
||||||
assert set(params.keys()) == set(['SAMLRequest', 'RelayState'])
|
assert set(params.keys()) == {'SAMLRequest', 'RelayState'}
|
||||||
assert len(params['SAMLRequest']) == 1
|
assert len(params['SAMLRequest']) == 1
|
||||||
assert base64.b64decode(params['SAMLRequest'][0])
|
assert base64.b64decode(params['SAMLRequest'][0])
|
||||||
assert client.session['mellon_next_url_%s' % params['RelayState'][0]] == '/whatever'
|
assert client.session['mellon_next_url_%s' % params['RelayState'][0]] == '/whatever'
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import include, url
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import include, url
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import include, url
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import django
|
import django
|
||||||
from django.conf import global_settings
|
from django.conf import global_settings
|
||||||
|
|
||||||
|
|
6
tox.ini
6
tox.ini
|
@ -1,5 +1,5 @@
|
||||||
[tox]
|
[tox]
|
||||||
envlist = black,py3-django22-coverage
|
envlist = code-style,py3-django22-coverage
|
||||||
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/django-mellon/
|
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/django-mellon/
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
|
@ -55,12 +55,12 @@ commands =
|
||||||
./getlasso3.sh
|
./getlasso3.sh
|
||||||
django-admin {posargs:--help}
|
django-admin {posargs:--help}
|
||||||
|
|
||||||
[testenv:black]
|
[testenv:code-style]
|
||||||
skip_install = true
|
skip_install = true
|
||||||
deps =
|
deps =
|
||||||
pre-commit
|
pre-commit
|
||||||
commands =
|
commands =
|
||||||
pre-commit run black --all-files --show-diff-on-failure
|
pre-commit run --all-files --show-diff-on-failure
|
||||||
|
|
||||||
[pytest]
|
[pytest]
|
||||||
junit_family=legacy
|
junit_family=legacy
|
||||||
|
|
Loading…
Reference in New Issue