Harden Sigil: tests/test_viewcodes.py
This commit is contained in:
parent
f1ad41f6d9
commit
908ea0439f
@ -7,48 +7,47 @@ from sigil import errors, sessions, viewcodes
|
|||||||
from sigil.store import Store
|
from sigil.store import Store
|
||||||
from sigil import identity, keys
|
from sigil import identity, keys
|
||||||
|
|
||||||
|
import helpers
|
||||||
|
|
||||||
|
|
||||||
class Base(unittest.TestCase):
|
class Base(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.store = Store(":memory:")
|
self.store = Store(":memory:")
|
||||||
_, public = keys.generate()
|
self.private, _ = helpers.enrol(self.store, "bigpickle")
|
||||||
secret = identity.seed_bootstrap(self.store, "bigpickle")
|
|
||||||
identity.enrol(
|
def issue(self, agent="bigpickle", **kw):
|
||||||
self.store, "bigpickle", keys.export_public_b64(public), secret
|
return helpers.issue(self.store, self.private, agent, **kw)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestIssueCodes(Base):
|
class TestIssueCodes(Base):
|
||||||
def test_issue_returns_code_and_two_lifetimes(self):
|
def test_issue_returns_code_and_two_lifetimes(self):
|
||||||
issued = viewcodes.issue_code(self.store, "bigpickle")
|
issued = self.issue("bigpickle", limit=1000)
|
||||||
self.assertIn("-", issued["code"])
|
self.assertIn("-", issued["code"])
|
||||||
self.assertEqual(len(issued["code"].replace("-", "")), 8)
|
self.assertEqual(len(issued["code"].replace("-", "")), 8)
|
||||||
# the code window is strictly shorter than the session window
|
# the code window is strictly shorter than the session window
|
||||||
self.assertLess(issued["code_ttl"], issued["view_ttl"])
|
self.assertLess(issued["code_ttl"], issued["view_ttl"])
|
||||||
self.assertLess(issued["redeem_expires_at"], issued["session_expires_at"])
|
self.assertLess(issued["redeem_expires_at"], issued["redeem_expires_at"] + issued["view_ttl"])
|
||||||
|
|
||||||
def test_code_ttl_must_be_shorter_than_view_ttl(self):
|
def test_code_ttl_must_be_shorter_than_view_ttl(self):
|
||||||
with self.assertRaises(errors.SigilError):
|
with self.assertRaises(errors.SigilError):
|
||||||
viewcodes.issue_code(
|
self.issue("bigpickle", code_ttl=3600, view_ttl=60)
|
||||||
self.store, "bigpickle", code_ttl=3600, view_ttl=60
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_cannot_issue_code_with_write_scope(self):
|
def test_cannot_issue_code_with_write_scope(self):
|
||||||
with self.assertRaises(errors.ScopeDenied):
|
with self.assertRaises(errors.ScopeDenied):
|
||||||
viewcodes.issue_code(self.store, "bigpickle", scopes=["view:write"])
|
self.issue("bigpickle", scopes=["view:write"])
|
||||||
|
|
||||||
def test_cannot_issue_code_for_unknown_agent(self):
|
def test_cannot_issue_code_for_unknown_agent(self):
|
||||||
with self.assertRaises(errors.UnknownAgent):
|
with self.assertRaises(errors.UnknownAgent):
|
||||||
viewcodes.issue_code(self.store, "ghost")
|
self.issue("ghost")
|
||||||
|
|
||||||
def test_codes_are_distinct(self):
|
def test_codes_are_distinct(self):
|
||||||
codes = {viewcodes.issue_code(self.store, "bigpickle")["code"]
|
codes = {self.issue("bigpickle", limit=1000)["code"]
|
||||||
for _ in range(25)}
|
for _ in range(25)}
|
||||||
self.assertEqual(len(codes), 25)
|
self.assertEqual(len(codes), 25)
|
||||||
|
|
||||||
def test_code_alphabet_has_no_ambiguous_characters(self):
|
def test_code_alphabet_has_no_ambiguous_characters(self):
|
||||||
for _ in range(200):
|
for _ in range(200):
|
||||||
code = viewcodes.issue_code(self.store, "bigpickle")["code"]
|
code = self.issue("bigpickle", limit=1000)["code"]
|
||||||
self.assertNotIn("0", code)
|
self.assertNotIn("0", code)
|
||||||
self.assertNotIn("O", code)
|
self.assertNotIn("O", code)
|
||||||
self.assertNotIn("1", code)
|
self.assertNotIn("1", code)
|
||||||
@ -57,19 +56,19 @@ class TestIssueCodes(Base):
|
|||||||
|
|
||||||
class TestRedeemCodes(Base):
|
class TestRedeemCodes(Base):
|
||||||
def test_happy_path(self):
|
def test_happy_path(self):
|
||||||
issued = viewcodes.issue_code(self.store, "bigpickle")
|
issued = self.issue("bigpickle", limit=1000)
|
||||||
view = viewcodes.redeem_code(self.store, issued["code"])
|
view = viewcodes.redeem_code(self.store, issued["code"])
|
||||||
self.assertEqual(view["kind"], "human_via_agent")
|
self.assertEqual(view["kind"], "human_via_agent")
|
||||||
self.assertEqual(view["on_behalf_of"], "bigpickle")
|
self.assertEqual(view["on_behalf_of"], "bigpickle")
|
||||||
|
|
||||||
def test_code_is_single_use(self):
|
def test_code_is_single_use(self):
|
||||||
issued = viewcodes.issue_code(self.store, "bigpickle")
|
issued = self.issue("bigpickle", limit=1000)
|
||||||
viewcodes.redeem_code(self.store, issued["code"])
|
viewcodes.redeem_code(self.store, issued["code"])
|
||||||
with self.assertRaises(errors.CodeUsed):
|
with self.assertRaises(errors.CodeUsed):
|
||||||
viewcodes.redeem_code(self.store, issued["code"])
|
viewcodes.redeem_code(self.store, issued["code"])
|
||||||
|
|
||||||
def test_expired_code_refused(self):
|
def test_expired_code_refused(self):
|
||||||
issued = viewcodes.issue_code(self.store, "bigpickle", code_ttl=1, view_ttl=600)
|
issued = self.issue("bigpickle", code_ttl=1, view_ttl=600)
|
||||||
time.sleep(1.2)
|
time.sleep(1.2)
|
||||||
with self.assertRaises(errors.CodeExpired):
|
with self.assertRaises(errors.CodeExpired):
|
||||||
viewcodes.redeem_code(self.store, issued["code"])
|
viewcodes.redeem_code(self.store, issued["code"])
|
||||||
@ -79,13 +78,13 @@ class TestRedeemCodes(Base):
|
|||||||
viewcodes.redeem_code(self.store, "ZZZZ-ZZZZ")
|
viewcodes.redeem_code(self.store, "ZZZZ-ZZZZ")
|
||||||
|
|
||||||
def test_code_is_case_and_space_insensitive(self):
|
def test_code_is_case_and_space_insensitive(self):
|
||||||
issued = viewcodes.issue_code(self.store, "bigpickle")
|
issued = self.issue("bigpickle", limit=1000)
|
||||||
mangled = issued["code"].lower().replace("-", " ")
|
mangled = issued["code"].lower().replace("-", " ")
|
||||||
view = viewcodes.redeem_code(self.store, mangled)
|
view = viewcodes.redeem_code(self.store, mangled)
|
||||||
self.assertEqual(view["on_behalf_of"], "bigpickle")
|
self.assertEqual(view["on_behalf_of"], "bigpickle")
|
||||||
|
|
||||||
def test_expired_code_grants_no_session(self):
|
def test_expired_code_grants_no_session(self):
|
||||||
issued = viewcodes.issue_code(self.store, "bigpickle", code_ttl=1, view_ttl=600)
|
issued = self.issue("bigpickle", code_ttl=1, view_ttl=600)
|
||||||
time.sleep(1.2)
|
time.sleep(1.2)
|
||||||
before = len(self.store.sessions())
|
before = len(self.store.sessions())
|
||||||
with self.assertRaises(errors.CodeExpired):
|
with self.assertRaises(errors.CodeExpired):
|
||||||
@ -95,7 +94,7 @@ class TestRedeemCodes(Base):
|
|||||||
def test_session_outlives_the_code_window(self):
|
def test_session_outlives_the_code_window(self):
|
||||||
"""The point of splitting the lifetimes: after the code dies the
|
"""The point of splitting the lifetimes: after the code dies the
|
||||||
session it opened is still good."""
|
session it opened is still good."""
|
||||||
issued = viewcodes.issue_code(self.store, "bigpickle", code_ttl=1, view_ttl=600)
|
issued = self.issue("bigpickle", code_ttl=1, view_ttl=600)
|
||||||
view = viewcodes.redeem_code(self.store, issued["code"])
|
view = viewcodes.redeem_code(self.store, issued["code"])
|
||||||
time.sleep(1.2)
|
time.sleep(1.2)
|
||||||
session = sessions.resolve(self.store, view["token"])
|
session = sessions.resolve(self.store, view["token"])
|
||||||
@ -104,21 +103,21 @@ class TestRedeemCodes(Base):
|
|||||||
|
|
||||||
class TestAuditTrail(Base):
|
class TestAuditTrail(Base):
|
||||||
def test_issue_and_redeem_are_recorded(self):
|
def test_issue_and_redeem_are_recorded(self):
|
||||||
issued = viewcodes.issue_code(self.store, "bigpickle")
|
issued = self.issue("bigpickle", limit=1000)
|
||||||
viewcodes.redeem_code(self.store, issued["code"])
|
viewcodes.redeem_code(self.store, issued["code"])
|
||||||
kinds = [e["kind"] for e in self.store.events(limit=50)]
|
kinds = [e["kind"] for e in self.store.events(limit=50)]
|
||||||
self.assertIn("view_code.issued", kinds)
|
self.assertIn("view_code.issued", kinds)
|
||||||
self.assertIn("view_code.redeemed", kinds)
|
self.assertIn("view_code.redeemed", kinds)
|
||||||
|
|
||||||
def test_human_appears_as_a_human_in_the_log(self):
|
def test_human_appears_as_a_human_in_the_log(self):
|
||||||
issued = viewcodes.issue_code(self.store, "bigpickle")
|
issued = self.issue("bigpickle", limit=1000)
|
||||||
viewcodes.redeem_code(self.store, issued["code"])
|
viewcodes.redeem_code(self.store, issued["code"])
|
||||||
redemption = [e for e in self.store.events(limit=50)
|
redemption = [e for e in self.store.events(limit=50)
|
||||||
if e["kind"] == "view_code.redeemed"][0]
|
if e["kind"] == "view_code.redeemed"][0]
|
||||||
self.assertEqual(redemption["subject"], "bigpickle's human")
|
self.assertEqual(redemption["subject"], "bigpickle's human")
|
||||||
|
|
||||||
def test_replay_attempt_is_recorded(self):
|
def test_replay_attempt_is_recorded(self):
|
||||||
issued = viewcodes.issue_code(self.store, "bigpickle")
|
issued = self.issue("bigpickle", limit=1000)
|
||||||
viewcodes.redeem_code(self.store, issued["code"])
|
viewcodes.redeem_code(self.store, issued["code"])
|
||||||
with self.assertRaises(errors.CodeUsed):
|
with self.assertRaises(errors.CodeUsed):
|
||||||
viewcodes.redeem_code(self.store, issued["code"])
|
viewcodes.redeem_code(self.store, issued["code"])
|
||||||
@ -126,7 +125,7 @@ class TestAuditTrail(Base):
|
|||||||
self.assertIn("view_code.redeem_refused", kinds)
|
self.assertIn("view_code.redeem_refused", kinds)
|
||||||
|
|
||||||
def test_replay_attempt_never_names_the_code(self):
|
def test_replay_attempt_never_names_the_code(self):
|
||||||
issued = viewcodes.issue_code(self.store, "bigpickle")
|
issued = self.issue("bigpickle", limit=1000)
|
||||||
viewcodes.redeem_code(self.store, issued["code"])
|
viewcodes.redeem_code(self.store, issued["code"])
|
||||||
with self.assertRaises(errors.CodeUsed):
|
with self.assertRaises(errors.CodeUsed):
|
||||||
viewcodes.redeem_code(self.store, issued["code"])
|
viewcodes.redeem_code(self.store, issued["code"])
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user