Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
663 changes: 663 additions & 0 deletions .tests/convert_old_logs.py

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions .tests/log-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"111": {"id": "int", "name": "string"},
"112": {"id": "int", "name": "string"},
"113": {"id": "int", "name": "string"},

"141": {"id": "int", "name": "string"},
"142": {"id": "int", "name": "string"},

"1B1": {"id": "string"},
"1B2": {"id": "string"},
"1B3": {"id": "string"},
"1B4": {"id": "string"},
"1B5": {"id": "string"},

"2F1": {"id": "int", "quantity": "int"},
"2F3": {"id": "int", "quantity": "int"},

"311": {"id": "string", "amount": "number"},
"313": {"id": "string", "amount": "number"},

"411": {}
}
2 changes: 1 addition & 1 deletion .tests/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@

if __name__ == "__main__":
decimal.DefaultContext.prec = 2
Launcher.launch("1.3.0")
Launcher.launch("1.3.1")
105 changes: 59 additions & 46 deletions .tests/tests/access_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import jwt
from utils.test_base import TestBase
from utils.auth import BearerAuth, BasicAuth, SecretKeyAuth
from .history_tests_helpers import HistoryTestHelpers
from .audit_tests_helpers import AuditTestHelpers
import base64

RE_DATE_FORMAT = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{1,9}Z")
Expand All @@ -11,13 +11,13 @@
class AccessTests(TestBase):
def setUp(self):
super().setUp()
self.history = HistoryTestHelpers(self)
self.audit = AuditTestHelpers(self)

def test_login_id_and_password(self):
auth = BasicAuth("lomens", "motdepasse")
key = "test-api-key-normal"

with self.history.watch():
with self.audit.watch():
response = self.post("login", auth=auth, headers={"X-Api-Key": key})

self.expect(response.status_code).to.be.equal_to(200)
Expand All @@ -32,12 +32,10 @@ def test_login_id_and_password(self):
user = self.expect(session).to.have.an_item("user").value
self.expect(user).to.have.an_item("id").that.is_.equal_to("lomens")

self.history.expect_entries(
self.history.login_action("Tests (normal)", "lomens")
)
self.audit.expect_entries(self.audit.entry("UserLoggedIn", 1, 1))

def test_login_id_and_password_fail(self):
with self.history.watch():
with self.audit.watch():
# missing api key

auth = BasicAuth("lomens", "motdepasse")
Expand All @@ -62,10 +60,10 @@ def test_login_id_and_password_fail(self):
response = self.post("login", auth=auth, headers={"X-Api-Key": key})
self.expect(response.status_code).to.be.equal_to(401)

self.history.expect_entries()
self.audit.expect_entries()

def test_login_permissions_normal(self):
with self.history.watch():
with self.audit.watch():
member_auth = BasicAuth("lomens", "motdepasse")
president_auth = BasicAuth("eb069420", "motdepasse")
key = "test-api-key-normal"
Expand Down Expand Up @@ -120,14 +118,20 @@ def test_login_permissions_normal(self):
)
self.expect(president_edit_response.status_code).to.not_.be.equal_to(403)

self.history.expect_entries(
self.history.login_action("Tests (normal)", "lomens"),
self.history.login_action("Tests (normal)", "eb069420"),
self.history.category_added_action("Catégorie", "eb069420"),
self.audit.expect_entries(
self.audit.entry("UserLoggedIn", 1, 1),
self.audit.entry("UserLoggedIn", 1, 3),
self.audit.entry(
"CategoryAdded",
1,
3,
id=president_edit_response.json()["id"],
name="Catégorie",
),
)

def test_login_permissions_restricted(self):
with self.history.watch():
with self.audit.watch():
member_auth = BasicAuth("lomens", "motdepasse")
president_auth = BasicAuth("eb069420", "motdepasse")
key = "test-api-key-restric"
Expand Down Expand Up @@ -183,13 +187,13 @@ def test_login_permissions_restricted(self):
# forbidden by api key
self.expect(president_edit_response.status_code).to.be.equal_to(403)

self.history.expect_entries(
self.history.login_action("Tests (restricted)", "lomens"),
self.history.login_action("Tests (restricted)", "eb069420"),
self.audit.expect_entries(
self.audit.entry("UserLoggedIn", 2, 1),
self.audit.entry("UserLoggedIn", 2, 3),
)

def test_login_permissions_minimum(self):
with self.history.watch():
with self.audit.watch():
member_auth = BasicAuth("lomens", "motdepasse")
president_auth = BasicAuth("eb069420", "motdepasse")
key = "test-api-key-minimum"
Expand Down Expand Up @@ -245,10 +249,16 @@ def test_login_permissions_minimum(self):
)
self.expect(president_edit_response.status_code).to.not_.be.equal_to(403)

self.history.expect_entries(
self.history.login_action("Tests (minimum)", "lomens"),
self.history.login_action("Tests (minimum)", "eb069420"),
self.history.category_added_action("Catégorie", "eb069420"),
self.audit.expect_entries(
self.audit.entry("UserLoggedIn", 3, 1),
self.audit.entry("UserLoggedIn", 3, 3),
self.audit.entry(
"CategoryAdded",
3,
3,
id=president_edit_response.json()["id"],
name="Catégorie",
),
)

def test_sso_direct(self):
Expand All @@ -260,7 +270,7 @@ def test_sso_direct(self):
"Password": "motdepasse",
}

with self.history.watch():
with self.audit.watch():
response = self.post(
"same-sign-on", json=login_data, headers={"X-Api-Key": galliumKey}
)
Expand Down Expand Up @@ -296,12 +306,13 @@ def test_sso_direct(self):
)
self.expect(full_redirect_url).to.be.equal_to(f"{redirect_url}?token={token}")

self.history.expect_entries(
self.history.login_sso_action(
"Tests (normal)",
"Tests (SSO, direct)",
"https://example.app/login",
"lomens",
self.audit.expect_entries(
self.audit.entry(
"SsoUserLoggedIn",
1,
iuid,
app=6,
redirectUrl="https://example.app/login",
)
)

Expand All @@ -318,7 +329,7 @@ def test_sso_external(self):
"Password": "motdepasse",
}

with self.history.watch():
with self.audit.watch():
response = self.post(
"same-sign-on", json=login_data, headers={"X-Api-Key": galliumKey}
)
Expand Down Expand Up @@ -353,12 +364,13 @@ def test_sso_external(self):
)
self.expect(full_redirect_url).to.be.equal_to(f"{redirect_url}?token={token}")

self.history.expect_entries(
self.history.login_sso_action(
"Tests (normal)",
"Tests (SSO, externe)",
"https://example.app/login",
"lomens",
self.audit.expect_entries(
self.audit.entry(
"SsoUserLoggedIn",
1,
iuid,
app=7,
redirectUrl="https://example.app/login",
)
)

Expand All @@ -371,7 +383,7 @@ def test_sso_and_bot(self):
"Password": "motdepasse",
}

with self.history.watch():
with self.audit.watch():
response = self.post(
"same-sign-on", json=login_data, headers={"X-Api-Key": galliumKey}
)
Expand Down Expand Up @@ -406,12 +418,13 @@ def test_sso_and_bot(self):
)
self.expect(full_redirect_url).to.be.equal_to(f"{redirect_url}?token={token}")

self.history.expect_entries(
self.history.login_sso_action(
"Tests (normal)",
"Tests (SSO, applicatif)",
"https://example.app/login",
"lomens",
self.audit.expect_entries(
self.audit.entry(
"SsoUserLoggedIn",
1,
iuid,
app=8,
redirectUrl="https://example.app/login",
)
)

Expand All @@ -422,7 +435,7 @@ def test_sso_and_bot(self):
self.expect(response.status_code).to.be.equal_to(200)

def test_login_app(self):
with self.history.watch():
with self.audit.watch():
app_auth = SecretKeyAuth("motdepasse")
key = "test-api-key-bot"

Expand All @@ -447,8 +460,8 @@ def test_login_app(self):
)
self.expect(edit_response.status_code).to.be.equal_to(403)

self.history.expect_entries(
self.history.app_login_action("Tests (bot)"),
self.audit.expect_entries(
self.audit.entry("ApplicationConnected", 5, None),
)

@staticmethod
Expand Down
34 changes: 22 additions & 12 deletions .tests/tests/audit_tests_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,22 @@
"UserDeleted": 0x1B3,
"UserDepositOpen": 0x1B4,
"UserDepositClosed": 0x1B5,
"ForcedStockIn": 0x2F1,
"ForcedStockOut": 0x2F3,
"AdvanceDeposited": 0x311,
"AdvanceWithdrawn": 0x313,
"UserLoggedIn": 0x411,
"ApplicationConnected": 0x412,
"SsoUserLoggedIn": 0x413,
}

REQUIRED = object()
DEFAULT = object()


class AuditTestHelpers:
def __init__(self, test_suite, client_id=None, user_id=None):
self.history = []
def __init__(self, test_suite, client_id=REQUIRED, user_id=REQUIRED):
self.logs = []
self.diff = []
self.test_suite = test_suite
self.client_id = client_id
Expand All @@ -56,13 +66,13 @@ def fetch(self):
self.test_suite.pop_authentication()
updated_history = response.json()

size_diff = len(updated_history) - len(self.history)
size_diff = len(updated_history) - len(self.logs)
if size_diff == 0:
self.diff = []
else:
self.diff = updated_history[-size_diff:]

self.history = updated_history
self.logs = updated_history

def watch(self):
return AuditingTestContext(self)
Expand All @@ -82,22 +92,22 @@ def expect_entries(self, *actions):
self.test_suite.assertEqual(actual["details"], expected["details"])

def resolve_client_id(self, arg):
if arg is not None:
if arg is not DEFAULT:
return arg
elif self.client_id is not None:
elif self.client_id is not REQUIRED:
return self.client_id
else:
raise RuntimeError("missing client ID")

def resolve_user_id(self, arg):
if arg is not None:
if arg is not DEFAULT:
return arg
elif self.user_id is not None:
elif self.user_id is not REQUIRED:
return self.user_id
else:
raise RuntimeError("missing user ID")

def entry(self, action, client_id=None, user_id=None, **details):
def entry(self, action, client_id=DEFAULT, user_id=DEFAULT, **details):
return {
"actionCode": ACTION_CODE_MAP[action],
"clientId": self.resolve_client_id(client_id),
Expand All @@ -108,11 +118,11 @@ def entry(self, action, client_id=None, user_id=None, **details):

class AuditingTestContext:
def __init__(self, auditLog):
self.auditLog = auditLog
self.logsLog = auditLog

def __enter__(self):
self.auditLog.fetch()
self.logsLog.fetch()
return self

def __exit__(self, exc_type, exc_value, trace):
self.auditLog.fetch()
self.logsLog.fetch()
Loading