mirror of
https://github.com/YunoHost/moulinette.git
synced 2024-09-03 20:06:31 +02:00
Implement basic tests for auth mechanism
This commit is contained in:
parent
82ba7e5b04
commit
2addea1e08
7 changed files with 184 additions and 1 deletions
28
moulinette/authenticators/dummy.py
Normal file
28
moulinette/authenticators/dummy.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
from moulinette.core import MoulinetteError
|
||||
from moulinette.authenticators import BaseAuthenticator
|
||||
|
||||
logger = logging.getLogger('moulinette.authenticator.dummy')
|
||||
|
||||
# Dummy authenticator implementation
|
||||
|
||||
|
||||
class Authenticator(BaseAuthenticator):
|
||||
|
||||
"""Dummy authenticator used for tests
|
||||
"""
|
||||
|
||||
vendor = 'dummy'
|
||||
|
||||
def __init__(self, name, vendor, parameters, extra):
|
||||
logger.debug("initialize authenticator '%s")
|
||||
super(Authenticator, self).__init__(name)
|
||||
|
||||
def authenticate(self, password):
|
||||
|
||||
if not password == "Yoloswag":
|
||||
raise MoulinetteError("Invalid password!")
|
||||
|
||||
return self
|
42
test/actionsmap/moulitest.yml
Normal file
42
test/actionsmap/moulitest.yml
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
#############################
|
||||
# Global parameters #
|
||||
#############################
|
||||
_global:
|
||||
configuration:
|
||||
authenticate:
|
||||
- all
|
||||
authenticator:
|
||||
default:
|
||||
vendor: dummy
|
||||
help: Dummy Password
|
||||
yoloswag:
|
||||
vendor: dummy
|
||||
help: Dummy Yoloswag Password
|
||||
|
||||
#############################
|
||||
# Test Actions #
|
||||
#############################
|
||||
testauth:
|
||||
actions:
|
||||
|
||||
none:
|
||||
api: GET /test-auth/none
|
||||
configuration:
|
||||
authenticate: false
|
||||
|
||||
default:
|
||||
api: GET /test-auth/default
|
||||
configuration:
|
||||
authenticate: all
|
||||
authenticator: default
|
||||
|
||||
# only-api:
|
||||
# api: GET /test-auth/only-api
|
||||
# configuration:
|
||||
# authenticate: api
|
||||
#
|
||||
other-profile:
|
||||
api: GET /test-auth/other-profile
|
||||
configuration:
|
||||
authenticator: yoloswag
|
|
@ -92,7 +92,6 @@ def patch_logging(moulinette):
|
|||
}
|
||||
|
||||
|
||||
|
||||
@pytest.fixture(scope='session', autouse=True)
|
||||
def moulinette(tmp_path_factory):
|
||||
import moulinette
|
||||
|
@ -100,8 +99,10 @@ def moulinette(tmp_path_factory):
|
|||
# Can't call the namespace just 'test' because
|
||||
# that would lead to some "import test" not importing the right stuff
|
||||
namespace = "moulitest"
|
||||
tmp_cache = str(tmp_path_factory.mktemp("cache"))
|
||||
tmp_data = str(tmp_path_factory.mktemp("data"))
|
||||
tmp_lib = str(tmp_path_factory.mktemp("lib"))
|
||||
os.environ['MOULINETTE_CACHE_DIR'] = tmp_cache
|
||||
os.environ['MOULINETTE_DATA_DIR'] = tmp_data
|
||||
os.environ['MOULINETTE_LIB_DIR'] = tmp_lib
|
||||
shutil.copytree("./test/actionsmap", "%s/actionsmap" % tmp_data)
|
||||
|
|
3
test/locales/en.json
Normal file
3
test/locales/en.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"foo": "bar"
|
||||
}
|
0
test/src/__init__.py
Normal file
0
test/src/__init__.py
Normal file
10
test/src/testauth.py
Normal file
10
test/src/testauth.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
def testauth_none():
|
||||
return "some_data_from_none"
|
||||
|
||||
|
||||
def testauth_default():
|
||||
return "some_data_from_default"
|
||||
|
||||
|
||||
def testauth_other_profile():
|
||||
return "some_data_from_other_profile"
|
99
test/test_auth.py
Normal file
99
test/test_auth.py
Normal file
|
@ -0,0 +1,99 @@
|
|||
import os
|
||||
import requests
|
||||
|
||||
|
||||
def login(webapi, cookies=None, csrf=False, profile=None):
|
||||
|
||||
data = {"password": "Yoloswag"}
|
||||
if profile:
|
||||
data["profile"] = profile
|
||||
|
||||
return requests.post(webapi + "/login",
|
||||
cookies=cookies,
|
||||
data=data,
|
||||
headers=None if csrf else {"X-Requested-With": ""})
|
||||
|
||||
|
||||
def test_request_no_auth_needed(monkeypatch, tmp_path, moulinette_webapi):
|
||||
|
||||
r = requests.get(moulinette_webapi + "/test-auth/none")
|
||||
|
||||
assert r.status_code == 200
|
||||
assert r.text == '"some_data_from_none"'
|
||||
|
||||
|
||||
def test_request_with_auth_but_not_logged(monkeypatch, tmp_path, moulinette_webapi):
|
||||
|
||||
r = requests.get(moulinette_webapi + "/test-auth/default")
|
||||
|
||||
assert r.status_code == 401
|
||||
assert r.text == "Authentication required"
|
||||
|
||||
|
||||
def test_login(monkeypatch, moulinette_webapi):
|
||||
|
||||
r = login(moulinette_webapi)
|
||||
|
||||
assert r.status_code == 200
|
||||
assert r.text == "Logged in"
|
||||
assert "session.id" in r.cookies
|
||||
assert "session.tokens" in r.cookies
|
||||
|
||||
cache_session_default = os.environ['MOULINETTE_CACHE_DIR'] + "/session/default/"
|
||||
assert r.cookies["session.id"] + ".asc" in os.listdir(cache_session_default)
|
||||
|
||||
|
||||
def test_login_csrf_attempt(moulinette_webapi):
|
||||
|
||||
# C.f.
|
||||
# https://security.stackexchange.com/a/58308
|
||||
# https://stackoverflow.com/a/22533680
|
||||
|
||||
r = login(moulinette_webapi, csrf=True)
|
||||
|
||||
assert r.status_code == 403
|
||||
assert "session.id" not in r.cookies
|
||||
assert "session.tokens" not in r.cookies
|
||||
assert "CSRF protection" in r.text
|
||||
|
||||
|
||||
def test_login_then_legit_request_without_cookies(moulinette_webapi):
|
||||
|
||||
login(moulinette_webapi)
|
||||
|
||||
r = requests.get(moulinette_webapi + "/test-auth/default")
|
||||
|
||||
assert r.status_code == 401
|
||||
assert r.text == "Authentication required"
|
||||
|
||||
|
||||
def test_login_then_legit_request(moulinette_webapi):
|
||||
|
||||
r_login = login(moulinette_webapi)
|
||||
|
||||
r = requests.get(moulinette_webapi + "/test-auth/default",
|
||||
cookies={"session.id": r_login.cookies["session.id"],
|
||||
"session.tokens": r_login.cookies["session.tokens"], })
|
||||
|
||||
assert r.status_code == 200
|
||||
assert r.text == '"some_data_from_default"'
|
||||
|
||||
|
||||
def test_login_then_logout(moulinette_webapi):
|
||||
|
||||
r_login = login(moulinette_webapi)
|
||||
|
||||
r = requests.get(moulinette_webapi + "/logout",
|
||||
cookies={"session.id": r_login.cookies["session.id"],
|
||||
"session.tokens": r_login.cookies["session.tokens"], })
|
||||
|
||||
assert r.status_code == 200
|
||||
cache_session_default = os.environ['MOULINETTE_CACHE_DIR'] + "/session/default/"
|
||||
assert not r_login.cookies["session.id"] + ".asc" in os.listdir(cache_session_default)
|
||||
|
||||
r = requests.get(moulinette_webapi + "/test-auth/default",
|
||||
cookies={"session.id": r_login.cookies["session.id"],
|
||||
"session.tokens": r_login.cookies["session.tokens"], })
|
||||
|
||||
assert r.status_code == 401
|
||||
assert r.text == "Authentication required"
|
Loading…
Reference in a new issue