Ajout type contrat
This commit is contained in:
0
venv/lib/python3.12/site-packages/tests/__init__.py
Normal file
0
venv/lib/python3.12/site-packages/tests/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
88
venv/lib/python3.12/site-packages/tests/settings.py
Normal file
88
venv/lib/python3.12/site-packages/tests/settings.py
Normal file
@@ -0,0 +1,88 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
urlpatterns = []
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': ':memory:'
|
||||
}
|
||||
}
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.messages',
|
||||
'simple_sso.sso_server',
|
||||
'simple_sso',
|
||||
'tests',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'tests.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [
|
||||
os.path.join(os.path.dirname(__file__), 'templates')
|
||||
],
|
||||
'OPTIONS': {
|
||||
'debug': True,
|
||||
'context_processors': [
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
'loaders': (
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
MIDDLEWARES = [
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
]
|
||||
|
||||
|
||||
def runtests():
|
||||
from django import setup
|
||||
from django.conf import settings
|
||||
from django.test.utils import get_runner
|
||||
|
||||
settings.configure(
|
||||
INSTALLED_APPS=INSTALLED_APPS,
|
||||
ROOT_URLCONF=ROOT_URLCONF,
|
||||
DATABASES=DATABASES,
|
||||
TEST_RUNNER='django.test.runner.DiscoverRunner',
|
||||
TEMPLATES=TEMPLATES,
|
||||
MIDDLEWARE=MIDDLEWARES,
|
||||
SSO_PRIVATE_KEY='private',
|
||||
SSO_PUBLIC_KEY='public',
|
||||
SSO_SERVER='http://localhost/server/',
|
||||
SECRET_KEY="secret-key-for-tests",
|
||||
)
|
||||
setup()
|
||||
|
||||
# Run the test suite, including the extra validation tests.
|
||||
TestRunner = get_runner(settings)
|
||||
|
||||
test_runner = TestRunner(verbosity=1, interactive=False, failfast=False)
|
||||
failures = test_runner.run_tests(INSTALLED_APPS)
|
||||
return failures
|
||||
|
||||
|
||||
def run():
|
||||
failures = runtests()
|
||||
sys.exit(failures)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
177
venv/lib/python3.12/site-packages/tests/test_core.py
Normal file
177
venv/lib/python3.12/site-packages/tests/test_core.py
Normal file
@@ -0,0 +1,177 @@
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user
|
||||
from django.contrib.auth.hashers import is_password_usable
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.test.testcases import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from simple_sso.sso_server.models import Token, Consumer
|
||||
from simple_sso.utils import gen_secret_key, SyncConsumer
|
||||
from tests.urls import test_client
|
||||
from tests.utils.context_managers import (SettingsOverride,
|
||||
UserLoginContext)
|
||||
|
||||
|
||||
class TestingConsumer(SyncConsumer):
|
||||
def __init__(self, test_client_, base_url, public_key, private_key):
|
||||
self.test_client = test_client_
|
||||
super(SyncConsumer, self).__init__(base_url, public_key, private_key)
|
||||
|
||||
def build_url(self, path):
|
||||
return path
|
||||
|
||||
def send_request(self, url, data, headers):
|
||||
headers = {
|
||||
'HTTP_%s' % header.upper().replace('-', '_'): value
|
||||
for header, value in headers.items()
|
||||
}
|
||||
response = self.test_client.post(
|
||||
url,
|
||||
data=data,
|
||||
content_type='application/json',
|
||||
**headers
|
||||
)
|
||||
self.raise_for_status(response.status_code, response.content)
|
||||
return response.content
|
||||
|
||||
|
||||
class SimpleSSOTests(TestCase):
|
||||
urls = 'simple_sso.test_urls'
|
||||
|
||||
def setUp(self):
|
||||
import requests
|
||||
|
||||
def get(url, params={}, headers={}, cookies=None, auth=None, **kwargs):
|
||||
return self.client.get(url, params)
|
||||
requests.get = get
|
||||
test_client.consumer = TestingConsumer(
|
||||
self.client, test_client.server_url, test_client.public_key, test_client.private_key)
|
||||
|
||||
def _get_consumer(self):
|
||||
return Consumer.objects.create(
|
||||
name='test',
|
||||
private_key=settings.SSO_PRIVATE_KEY,
|
||||
public_key=settings.SSO_PUBLIC_KEY,
|
||||
)
|
||||
|
||||
def test_walkthrough(self):
|
||||
USERNAME = PASSWORD = 'myuser'
|
||||
server_user = User.objects.create_user(USERNAME, 'my@user.com', PASSWORD)
|
||||
self._get_consumer()
|
||||
# verify theres no tokens yet
|
||||
self.assertEqual(Token.objects.count(), 0)
|
||||
response = self.client.get(reverse('simple-sso-login'))
|
||||
# there should be a token now
|
||||
self.assertEqual(Token.objects.count(), 1)
|
||||
# this should be a HttpResponseRedirect
|
||||
self.assertEqual(response.status_code, HttpResponseRedirect.status_code)
|
||||
# check that it's the URL we expect
|
||||
url = urlparse(response['Location'])
|
||||
path = url.path
|
||||
self.assertEqual(path, reverse('simple-sso-authorize'))
|
||||
# follow that redirect
|
||||
response = self.client.get(response['Location'])
|
||||
# now we should have another redirect to the login
|
||||
self.assertEqual(response.status_code, HttpResponseRedirect.status_code, response.content)
|
||||
# check that the URL is correct
|
||||
url = urlparse(response['Location'])
|
||||
path = url.path
|
||||
self.assertEqual(path, reverse('login'))
|
||||
# follow that redirect
|
||||
login_url = response['Location']
|
||||
response = self.client.get(login_url)
|
||||
# now we should have a 200
|
||||
self.assertEqual(response.status_code, HttpResponse.status_code)
|
||||
# and log in using the username/password from above
|
||||
response = self.client.post(login_url, {'username': USERNAME, 'password': PASSWORD})
|
||||
# now we should have a redirect back to the authorize view
|
||||
self.assertEqual(response.status_code, HttpResponseRedirect.status_code)
|
||||
# check that it's the URL we expect
|
||||
url = urlparse(response['Location'])
|
||||
path = url.path
|
||||
self.assertEqual(path, reverse('simple-sso-authorize'))
|
||||
# follow that redirect
|
||||
response = self.client.get(response['Location'])
|
||||
# this should again be a redirect
|
||||
self.assertEqual(response.status_code, HttpResponseRedirect.status_code)
|
||||
# this time back to the client app, confirm that!
|
||||
url = urlparse(response['Location'])
|
||||
path = url.path
|
||||
self.assertEqual(path, reverse('simple-sso-authenticate'))
|
||||
# follow it again
|
||||
response = self.client.get(response['Location'])
|
||||
# again a redirect! This time to /
|
||||
url = urlparse(response['Location'])
|
||||
path = url.path
|
||||
self.assertEqual(path, reverse('root'))
|
||||
# if we follow to root now, we should be logged in
|
||||
response = self.client.get(response['Location'])
|
||||
client_user = get_user(self.client)
|
||||
self.assertFalse(is_password_usable(client_user.password))
|
||||
self.assertTrue(is_password_usable(server_user.password))
|
||||
for key in ['username', 'email', 'first_name', 'last_name']:
|
||||
self.assertEqual(getattr(client_user, key), getattr(server_user, key))
|
||||
|
||||
def test_user_already_logged_in(self):
|
||||
USERNAME = PASSWORD = 'myuser'
|
||||
server_user = User.objects.create_user(USERNAME, 'my@user.com', PASSWORD)
|
||||
self._get_consumer()
|
||||
with UserLoginContext(self, server_user):
|
||||
# try logging in and auto-follow all 302s
|
||||
self.client.get(reverse('simple-sso-login'), follow=True)
|
||||
# check the user
|
||||
client_user = get_user(self.client)
|
||||
self.assertFalse(is_password_usable(client_user.password))
|
||||
self.assertTrue(is_password_usable(server_user.password))
|
||||
for key in ['username', 'email', 'first_name', 'last_name']:
|
||||
self.assertEqual(getattr(client_user, key), getattr(server_user, key))
|
||||
|
||||
def test_user_data_updated(self):
|
||||
""" User data update test
|
||||
|
||||
Tests whether sso server user data changes will be forwared to the client on the user's next login.
|
||||
|
||||
"""
|
||||
USERNAME = PASSWORD = 'myuser'
|
||||
extra_data = {
|
||||
"first_name": "bob",
|
||||
"last_name": "bobster",
|
||||
}
|
||||
server_user = User.objects.create_user(
|
||||
USERNAME,
|
||||
'bob@bobster.org',
|
||||
PASSWORD,
|
||||
**extra_data,
|
||||
)
|
||||
self._get_consumer()
|
||||
|
||||
with UserLoginContext(self, server_user):
|
||||
# First login
|
||||
# try logging in and auto-follow all 302s
|
||||
self.client.get(reverse('simple-sso-login'), follow=True)
|
||||
# check the user
|
||||
client_user = get_user(self.client)
|
||||
for key in ['username', 'email', 'first_name', 'last_name']:
|
||||
self.assertEqual(getattr(client_user, key), getattr(server_user, key))
|
||||
|
||||
# User data changes
|
||||
server_user.first_name = "Alice"
|
||||
server_user.email = "alice@bobster.org"
|
||||
server_user.save()
|
||||
|
||||
with UserLoginContext(self, server_user):
|
||||
# Second login
|
||||
self.client.get(reverse('simple-sso-login'), follow=True)
|
||||
client_user = get_user(self.client)
|
||||
for key in ['username', 'email', 'first_name', 'last_name']:
|
||||
self.assertEqual(getattr(client_user, key), getattr(server_user, key))
|
||||
|
||||
def test_custom_keygen(self):
|
||||
# WARNING: The following test uses a key generator function that is
|
||||
# highly insecure and should never under any circumstances be used in
|
||||
# a production enivornment
|
||||
with SettingsOverride(SIMPLE_SSO_KEYGENERATOR=lambda length: 'test'):
|
||||
self.assertEqual(gen_secret_key(40), 'test')
|
||||
30
venv/lib/python3.12/site-packages/tests/test_migrations.py
Normal file
30
venv/lib/python3.12/site-packages/tests/test_migrations.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# original from
|
||||
# http://tech.octopus.energy/news/2016/01/21/testing-for-missing-migrations-in-django.html
|
||||
from io import StringIO
|
||||
|
||||
from django.core.management import call_command
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
|
||||
class MigrationTestCase(TestCase):
|
||||
|
||||
@override_settings(MIGRATION_MODULES={})
|
||||
def test_for_missing_migrations(self):
|
||||
output = StringIO()
|
||||
options = {
|
||||
'interactive': False,
|
||||
'dry_run': True,
|
||||
'stdout': output,
|
||||
'check_changes': True,
|
||||
}
|
||||
|
||||
try:
|
||||
call_command('makemigrations', **options)
|
||||
except SystemExit as e:
|
||||
status_code = str(e)
|
||||
else:
|
||||
# the "no changes" exit code is 0
|
||||
status_code = '0'
|
||||
|
||||
if status_code == '1':
|
||||
self.fail('There are missing migrations:\n {}'.format(output.getvalue()))
|
||||
19
venv/lib/python3.12/site-packages/tests/urls.py
Normal file
19
venv/lib/python3.12/site-packages/tests/urls.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.views import LoginView
|
||||
from django.http import HttpResponse
|
||||
from django.urls import re_path, include
|
||||
|
||||
from simple_sso.sso_client.client import Client
|
||||
from simple_sso.sso_server.server import Server
|
||||
|
||||
test_server = Server()
|
||||
test_client = Client(settings.SSO_SERVER, settings.SSO_PUBLIC_KEY, settings.SSO_PRIVATE_KEY)
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^admin/', admin.site.urls),
|
||||
re_path(r'^server/', include(test_server.get_urls())),
|
||||
re_path(r'^client/', include(test_client.get_urls())),
|
||||
re_path(r'^login/$', LoginView.as_view(template_name='admin/login.html'), name="login"),
|
||||
re_path('^$', lambda request: HttpResponse('home'), name='root')
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,52 @@
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class NULL:
|
||||
pass
|
||||
|
||||
|
||||
class SettingsOverride:
|
||||
"""
|
||||
Overrides Django settings within a context and resets them to their inital
|
||||
values on exit.
|
||||
|
||||
Example:
|
||||
|
||||
with SettingsOverride(DEBUG=True):
|
||||
# do something
|
||||
"""
|
||||
|
||||
def __init__(self, **overrides):
|
||||
self.overrides = overrides
|
||||
|
||||
def __enter__(self):
|
||||
self.old = {}
|
||||
for key, value in self.overrides.items():
|
||||
self.old[key] = getattr(settings, key, NULL)
|
||||
setattr(settings, key, value)
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
for key, value in self.old.items():
|
||||
if value is not NULL:
|
||||
setattr(settings, key, value)
|
||||
else:
|
||||
delattr(settings, key) # do not pollute the context!
|
||||
|
||||
|
||||
class UserLoginContext:
|
||||
def __init__(self, testcase, user):
|
||||
self.testcase = testcase
|
||||
self.user = user
|
||||
|
||||
def __enter__(self):
|
||||
loginok = self.testcase.client.login(username=self.user.username,
|
||||
password=self.user.username)
|
||||
self.old_user = getattr(self.testcase, 'user', None)
|
||||
self.testcase.user = self.user
|
||||
self.testcase.assertTrue(loginok)
|
||||
|
||||
def __exit__(self, exc, value, tb):
|
||||
self.testcase.user = self.old_user
|
||||
if not self.testcase.user:
|
||||
delattr(self.testcase, 'user')
|
||||
self.testcase.client.logout()
|
||||
Reference in New Issue
Block a user