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)
|
@pytest.fixture(scope='session', autouse=True)
|
||||||
def moulinette(tmp_path_factory):
|
def moulinette(tmp_path_factory):
|
||||||
import moulinette
|
import moulinette
|
||||||
|
@ -100,8 +99,10 @@ def moulinette(tmp_path_factory):
|
||||||
# Can't call the namespace just 'test' because
|
# Can't call the namespace just 'test' because
|
||||||
# that would lead to some "import test" not importing the right stuff
|
# that would lead to some "import test" not importing the right stuff
|
||||||
namespace = "moulitest"
|
namespace = "moulitest"
|
||||||
|
tmp_cache = str(tmp_path_factory.mktemp("cache"))
|
||||||
tmp_data = str(tmp_path_factory.mktemp("data"))
|
tmp_data = str(tmp_path_factory.mktemp("data"))
|
||||||
tmp_lib = str(tmp_path_factory.mktemp("lib"))
|
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_DATA_DIR'] = tmp_data
|
||||||
os.environ['MOULINETTE_LIB_DIR'] = tmp_lib
|
os.environ['MOULINETTE_LIB_DIR'] = tmp_lib
|
||||||
shutil.copytree("./test/actionsmap", "%s/actionsmap" % tmp_data)
|
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