Esempio n. 1
0
    def test_missingRequiredParameter(self):
        # type: () -> None
        """
        If required fields are missing, a default error form is presented and
        the form's handler is not called.
        """
        mem = MemorySessionStore()

        session = self.successResultOf(
            mem.newSession(True, SessionMechanism.Header))

        to = TestObject(mem)
        stub = StubTreq(to.router.resource())
        response = self.successResultOf(
            stub.post(
                "https://localhost/handle",
                data=dict(),
                headers={b"X-Test-Session": session.identifier},
            ))
        self.assertEqual(response.code, 400)
        self.assertIn(
            b"a value was required but none was supplied",
            self.successResultOf(content(response)),
        )
        self.assertEqual(to.calls, [])
Esempio n. 2
0
    def test_numberConstraints(self):
        # type: () -> None
        """
        Number parameters have minimum and maximum validations and the object
        will not be called when the values exceed them.
        """
        mem = MemorySessionStore()

        session = self.successResultOf(
            mem.newSession(True, SessionMechanism.Header))

        to = TestObject(mem)
        stub = StubTreq(to.router.resource())
        tooLow = self.successResultOf(
            stub.post('https://localhost/constrained',
                      data=dict(goldilocks='1'),
                      headers={b'X-Test-Session': session.identifier}))
        tooHigh = self.successResultOf(
            stub.post('https://localhost/constrained',
                      data=dict(goldilocks='20'),
                      headers={b'X-Test-Session': session.identifier}))
        justRight = self.successResultOf(
            stub.post('https://localhost/constrained',
                      data=dict(goldilocks='7'),
                      headers={b'X-Test-Session': session.identifier}))

        self.assertEqual(tooHigh.code, 400)
        self.assertEqual(tooLow.code, 400)
        self.assertEqual(justRight.code, 200)
        self.assertEqual(self.successResultOf(content(justRight)), b'got it')
        self.assertEqual(to.calls, [(u'constrained', 7)])
Esempio n. 3
0
    def test_missingOptionalParameterJSON(self):
        # type: () -> None
        """
        If a required Field is missing from the JSON body, its default value is
        used.
        """
        mem = MemorySessionStore()

        session = self.successResultOf(
            mem.newSession(True, SessionMechanism.Header))

        to = TestObject(mem)
        stub = StubTreq(to.router.resource())
        response = self.successResultOf(
            stub.post(
                "https://localhost/notrequired",
                json=dict(name="one"),
                headers={b"X-Test-Session": session.identifier},
            ))
        response2 = self.successResultOf(
            stub.post(
                "https://localhost/notrequired",
                json=dict(name="two", value=2),
                headers={b"X-Test-Session": session.identifier},
            ))
        self.assertEqual(response.code, 200)
        self.assertEqual(response2.code, 200)
        self.assertEqual(self.successResultOf(content(response)), b"okay")
        self.assertEqual(self.successResultOf(content(response2)), b"okay")
        self.assertEqual(to.calls, [("one", 7.0), ("two", 2.0)])
Esempio n. 4
0
    def test_renderingFormGlue(self):
        # type: () -> None
        """
        When a form renderer renders just the glue, none of the rest of the
        form is included.
        """
        mem = MemorySessionStore()

        session = self.successResultOf(
            mem.newSession(True, SessionMechanism.Header))

        stub = StubTreq(TestObject(mem).router.resource())
        response = self.successResultOf(
            stub.get(
                "https://localhost/render-custom",
                headers={b"X-Test-Session": session.identifier},
            ))
        self.assertEqual(response.code, 200)
        self.assertIn(
            response.headers.getRawHeaders(b"content-type")[0], b"text/html")
        responseDom = ElementTree.fromstring(
            self.successResultOf(content(response)))
        submitButton = responseDom.findall(".//*[@type='submit']")
        self.assertEqual(len(submitButton), 0)
        protectionField = responseDom.findall(
            ".//*[@name='__csrf_protection__']")
        self.assertEqual(protectionField[0].attrib["value"],
                         session.identifier)
Esempio n. 5
0
    def test_rendering(self):
        # type: () -> None
        """
        When a route requires form fields, it renders a form with those fields.
        """
        mem = MemorySessionStore()

        session = self.successResultOf(
            mem.newSession(True, SessionMechanism.Header))

        stub = StubTreq(TestObject(mem).router.resource())
        response = self.successResultOf(
            stub.get(
                "https://localhost/render",
                headers={b"X-Test-Session": session.identifier},
            ))
        self.assertEqual(response.code, 200)
        self.assertIn(
            response.headers.getRawHeaders(b"content-type")[0], b"text/html")
        responseDom = ElementTree.fromstring(
            self.successResultOf(content(response)))
        submitButton = responseDom.findall(".//*[@type='submit']")
        self.assertEqual(len(submitButton), 1)
        self.assertEqual(submitButton[0].attrib["name"],
                         "__klein_auto_submit__")
Esempio n. 6
0
    def test_renderingEmptyForm(self):
        # type: () -> None
        """
        When a form renderer specifies a submit button, no automatic submit
        button is rendered.
        """
        mem = MemorySessionStore()

        session = self.successResultOf(
            mem.newSession(True, SessionMechanism.Header))

        stub = StubTreq(TestObject(mem).router.resource())
        response = self.successResultOf(
            stub.get(
                "https://localhost/render-empty",
                headers={b"X-Test-Session": session.identifier},
            ))
        self.assertEqual(response.code, 200)
        self.assertIn(
            response.headers.getRawHeaders(b"content-type")[0], b"text/html")
        responseDom = ElementTree.fromstring(
            self.successResultOf(content(response)))
        submitButton = responseDom.findall(".//*[@type='submit']")
        self.assertEqual(len(submitButton), 1)
        self.assertEqual(submitButton[0].attrib["name"],
                         "__klein_auto_submit__")
        protectionField = responseDom.findall(
            ".//*[@name='__csrf_protection__']")
        self.assertEqual(protectionField[0].attrib["value"],
                         session.identifier)
Esempio n. 7
0
    def test_customValidationHandling(self):
        # type: () -> None
        """
        L{Form.onValidationFailureFor} handles form validation failures by
        handing its thing a renderable form.
        """
        mem = MemorySessionStore()

        session = self.successResultOf(
            mem.newSession(True, SessionMechanism.Header))

        testobj = TestObject(mem)
        stub = StubTreq(testobj.router.resource())
        response = self.successResultOf(
            stub.post(
                "https://localhost/handle-validation",
                headers={b"X-Test-Session": session.identifier},
                json={"value": 300},
            ))
        self.assertEqual(response.code, 200)
        self.assertIn(
            response.headers.getRawHeaders(b"content-type")[0], b"text/html")
        responseText = self.successResultOf(content(response))
        self.assertEqual(responseText, b"~special~")
        self.assertEqual(
            [(k.pythonArgumentName, v)
             for k, v in testobj.calls[-1][1].prevalidationValues.items()],
            [("value", 300)],
        )
Esempio n. 8
0
 def test_cookieWithToken(self):
     # type: () -> None
     """
     A cookie-authenticated, CRSF-protected form will call the form as
     expected.
     """
     mem = MemorySessionStore()
     session = self.successResultOf(
         mem.newSession(True, SessionMechanism.Cookie))
     to = TestObject(mem)
     stub = StubTreq(to.router.resource())
     response = self.successResultOf(
         stub.post(
             "https://localhost/handle",
             data=dict(
                 name="hello",
                 value="1234",
                 ignoreme="extraneous",
                 __csrf_protection__=session.identifier,
             ),
             cookies={
                 "Klein-Secure-Session": nativeString(session.identifier)
             },
         ))
     self.assertEqual(to.calls, [("hello", 1234)])
     self.assertEqual(response.code, 200)
     self.assertIn(b"yay", self.successResultOf(content(response)))
Esempio n. 9
0
 def test_noAuthorizers(self) -> None:
     """
     By default, L{MemorySessionStore} contains no authorizers and the
     sessions it returns will authorize any supplied interfaces as None.
     """
     store = MemorySessionStore()
     session = self.successResultOf(
         store.newSession(True, SessionMechanism.Header))
     self.assertEqual(self.successResultOf(session.authorize([IFoo, IBar])),
                      {})
Esempio n. 10
0
 def test_interfaceCompliance(self):
     # type: () -> None
     """
     Verify that the session store complies with the relevant interfaces.
     """
     store = MemorySessionStore()
     verifyObject(ISessionStore, store)  # type: ignore[misc]
     verifyObject(
         ISession,
         self.successResultOf(  # type: ignore[misc]
             store.newSession(True, SessionMechanism.Header)))
Esempio n. 11
0
    def test_simpleAuthorization(self):
        # type: () -> None
        """
        L{MemorySessionStore.fromAuthorizers} takes a set of functions
        decorated with L{declareMemoryAuthorizer} and constructs a session
        store that can authorize for those interfaces.
        """
        @declareMemoryAuthorizer(IFoo)
        def fooMe(interface, session, componentized):
            # type: (Any, Any, Any) -> int
            return 1

        @declareMemoryAuthorizer(IBar)
        def barMe(interface, session, componentized):
            # type: (Any, Any, Any) -> int
            return 2

        store = MemorySessionStore.fromAuthorizers([fooMe, barMe])
        session = self.successResultOf(
            store.newSession(False, SessionMechanism.Cookie))
        self.assertEqual(self.successResultOf(session.authorize([IBar, IFoo])),
                         {
                             IFoo: 1,
                             IBar: 2
                         })
Esempio n. 12
0
    def test_procurementSecurity(self) -> None:
        """
        Once a session is negotiated, it should be the identical object to
        avoid duplicate work - unless we are using forceInsecure to retrieve
        the insecure session from a secure request, in which case the result
        should not be cached.
        """
        sessions = []
        mss = MemorySessionStore()
        router = Klein()

        @router.route("/")
        @inlineCallbacks
        def route(request: IRequest) -> Deferred:
            sproc = SessionProcurer(mss)
            sessions.append((yield sproc.procureSession(request)))
            sessions.append((yield sproc.procureSession(request)))
            sessions.append((yield sproc.procureSession(request,
                                                        forceInsecure=True)))
            returnValue(b"sessioned")

        treq = StubTreq(router.resource())
        self.successResultOf(treq.get("http://unittest.example.com/"))
        self.assertIs(sessions[0], sessions[1])
        self.assertIs(sessions[0], sessions[2])
        self.successResultOf(treq.get("https://unittest.example.com/"))
        self.assertIs(sessions[3], sessions[4])
        self.assertIsNot(sessions[3], sessions[5])
Esempio n. 13
0
 def test_cookieNoToken(self):
     # type: () -> None
     """
     A cookie-authenticated, CSRF-protected form will return a 403 Forbidden
     status code when a CSRF protection token is not supplied.
     """
     mem = MemorySessionStore()
     session = self.successResultOf(
         mem.newSession(True, SessionMechanism.Cookie)
     )
     to = TestObject(mem)
     stub = StubTreq(to.router.resource())
     response = self.successResultOf(stub.post(
         'https://localhost/handle',
         data=dict(name='hello', value='1234', ignoreme='extraneous'),
         cookies={"Klein-Secure-Session": nativeString(session.identifier)}
     ))
     self.assertEqual(to.calls, [])
     self.assertEqual(response.code, 403)
     self.assertIn(b'CSRF', self.successResultOf(content(response)))
Esempio n. 14
0
 def test_noName(self):
     # type: () -> None
     """
     A handler for a Form with a Field that doesn't have a name will return
     an error explaining the problem.
     """
     mem = MemorySessionStore()
     session = self.successResultOf(
         mem.newSession(True, SessionMechanism.Header))
     to = TestObject(mem)
     stub = StubTreq(to.router.resource())
     response = self.successResultOf(
         stub.post('https://localhost/dangling-param',
                   data=dict(),
                   headers={b'X-Test-Session': session.identifier}))
     self.assertEqual(response.code, 500)
     errors = self.flushLoggedErrors(ValueError)
     self.assertEqual(len(errors), 1)
     self.assertIn(str(errors[0].value),
                   "Cannot extract unnamed form field.")
Esempio n. 15
0
    def test_renderLookupError(self):
        # type: () -> None
        """
        RenderableForm raises L{MissingRenderMethod} if anything attempst to
        look up a render method on it.
        """
        mem = MemorySessionStore()

        session = self.successResultOf(
            mem.newSession(True, SessionMechanism.Header))

        stub = StubTreq(TestObject(mem).router.resource())
        response = self.successResultOf(
            stub.get('https://localhost/render-cascade',
                     headers={b'X-Test-Session': session.identifier}))
        self.assertEqual(response.code, 200)
        # print(self.successResultOf(response.content()).decode('utf-8'))
        failures = self.flushLoggedErrors()
        self.assertEqual(len(failures), 1)
        self.assertIn("MissingRenderMethod", str(failures[0]))
Esempio n. 16
0
    def test_handlingPassword(self):
        # type: () -> None
        """
        From the perspective of form handling, passwords are handled like
        strings.
        """
        mem = MemorySessionStore()

        session = self.successResultOf(
            mem.newSession(True, SessionMechanism.Header))

        to = TestObject(mem)
        stub = StubTreq(to.router.resource())
        response = self.successResultOf(
            stub.post('https://localhost/password-field',
                      data=dict(pw='asdfjkl;'),
                      headers={b'X-Test-Session': session.identifier}))
        self.assertEqual(response.code, 200)
        self.assertEqual(self.successResultOf(content(response)),
                         b'password received')
        self.assertEqual(to.calls, [(u'password', u'asdfjkl;')])
Esempio n. 17
0
    def test_handlingJSON(self) -> None:
        """
        A handler for a form with Fields receives those fields as input, as
        passed by an HTTP client that submits a JSON POST body.
        """
        mem = MemorySessionStore()

        session = self.successResultOf(
            mem.newSession(True, SessionMechanism.Header))

        to = TestObject(mem)
        stub = StubTreq(to.router.resource())
        response = self.successResultOf(
            stub.post(
                "https://localhost/handle",
                json=dict(name="hello", value="1234", ignoreme="extraneous"),
                headers={"X-Test-Session": session.identifier},
            ))
        self.assertEqual(response.code, 200)
        self.assertEqual(self.successResultOf(content(response)), b"yay")
        self.assertEqual(to.calls, [("hello", 1234)])
Esempio n. 18
0
 def test_renderingWithNoSessionYet(self) -> None:
     """
     When a route is rendered with no session, it sets a cookie to establish
     a new session.
     """
     mem = MemorySessionStore()
     stub = StubTreq(TestObject(mem).router.resource())
     response = self.successResultOf(stub.get("https://localhost/render"))
     self.assertEqual(response.code, 200)
     setCookie = response.cookies()["Klein-Secure-Session"]
     expected = f'value="{setCookie}"'
     actual = self.successResultOf(content(response)).decode("utf-8")
     self.assertIn(expected, actual)
Esempio n. 19
0
    def test_handling(self):
        # type: () -> None
        """
        A handler for a Form with Fields receives those fields as input, as
        passed by an HTTP client.
        """
        mem = MemorySessionStore()

        session = self.successResultOf(
            mem.newSession(True, SessionMechanism.Header))

        to = TestObject(mem)
        stub = StubTreq(to.router.resource())
        response = self.successResultOf(
            stub.post('https://localhost/handle',
                      data=dict(name='hello',
                                value='1234',
                                ignoreme='extraneous'),
                      headers={b'X-Test-Session': session.identifier}))
        self.assertEqual(response.code, 200)
        self.assertEqual(self.successResultOf(content(response)), b'yay')
        self.assertEqual(to.calls, [(u'hello', 1234)])
Esempio n. 20
0
    def test_renderingExplicitSubmit(self):
        # type: () -> None
        """
        When a form renderer specifies a submit button, no automatic submit
        button is rendered.
        """
        mem = MemorySessionStore()

        session = self.successResultOf(
            mem.newSession(True, SessionMechanism.Header))

        stub = StubTreq(TestObject(mem).router.resource())
        response = self.successResultOf(
            stub.get('https://localhost/render-submit',
                     headers={b'X-Test-Session': session.identifier}))
        self.assertEqual(response.code, 200)
        self.assertIn(
            response.headers.getRawHeaders(b"content-type")[0], b"text/html")
        responseDom = ElementTree.fromstring(
            self.successResultOf(content(response)))
        submitButton = responseDom.findall(".//*[@type='submit']")
        self.assertEqual(len(submitButton), 1)
        self.assertEqual(submitButton[0].attrib['name'], 'button')
Esempio n. 21
0
 def test_noSessionPOST(self):
     # type: () -> None
     """
     An unauthenticated, CSRF-protected form will return a 403 Forbidden
     status code.
     """
     mem = MemorySessionStore()
     to = TestObject(mem)
     stub = StubTreq(to.router.resource())
     response = self.successResultOf(
         stub.post('https://localhost/handle',
                   data=dict(name='hello', value='1234')))
     self.assertEqual(to.calls, [])
     self.assertEqual(response.code, 403)
     self.assertIn(b'CSRF', self.successResultOf(content(response)))
Esempio n. 22
0
 def test_renderingWithNoSessionYet(self):
     # type: () -> None
     """
     When a route is rendered with no session, it sets a cookie to establish
     a new session.
     """
     mem = MemorySessionStore()
     stub = StubTreq(TestObject(mem).router.resource())
     response = self.successResultOf(stub.get("https://localhost/render"))
     self.assertEqual(response.code, 200)
     setCookie = response.cookies()["Klein-Secure-Session"]
     expected = 'value="{}"'.format(setCookie)
     actual = self.successResultOf(content(response))
     if not isinstance(expected, bytes):  # type: ignore[unreachable]
         actual = actual.decode("utf-8")
     self.assertIn(expected, actual)
Esempio n. 23
0
def simpleSessionRouter():
    # type: () -> Tuple[sessions, errors, str, str, StubTreq]
    """
    Construct a simple router.
    """
    sessions = []
    exceptions = []
    mss = MemorySessionStore.fromAuthorizers([memoryAuthorizer])
    router = Klein()
    token = "X-Test-Session-Token"
    cookie = "X-Test-Session-Cookie"
    sproc = SessionProcurer(
        mss,
        secureTokenHeader=b"X-Test-Session-Token",
        secureCookie=b"X-Test-Session-Cookie",
    )

    @router.route("/")
    @inlineCallbacks
    def route(request):
        # type: (IRequest) -> Deferred
        try:
            sessions.append((yield sproc.procureSession(request)))
        except NoSuchSession as nss:
            exceptions.append(nss)
        returnValue(b"ok")

    requirer = Requirer()

    @requirer.prerequisite([ISession])
    def procure(request):
        # type: (IRequest) -> Deferred
        return sproc.procureSession(request)

    @requirer.require(router.route("/test"), simple=Authorization(ISimpleTest))
    def testRoute(simple):
        # type: (SimpleTest) -> str
        return "ok: " + str(simple.doTest() + 4)

    @requirer.require(router.route("/denied"), nope=Authorization(IDenyMe))
    def testDenied(nope):
        # type: (IDenyMe) -> str
        return "bad"

    treq = StubTreq(router.resource())
    return sessions, exceptions, token, cookie, treq
Esempio n. 24
0
    def test_cookiesTurnedOff(self) -> None:
        """
        If cookies can't be set, then C{procureSession} raises
        L{NoSuchSession}.
        """
        mss = MemorySessionStore()
        router = Klein()

        @router.route("/")
        @inlineCallbacks
        def route(request: IRequest) -> Deferred:
            sproc = SessionProcurer(mss, setCookieOnGET=False)
            with self.assertRaises(NoSuchSession):
                yield sproc.procureSession(request)
            returnValue(b"no session")

        treq = StubTreq(router.resource())
        result = self.successResultOf(treq.get("http://unittest.example.com/"))
        self.assertEqual(self.successResultOf(result.content()), b"no session")
Esempio n. 25
0
    def test_procuredTooLate(self) -> None:
        """
        If you start writing stuff to the response before procuring the
        session, when cookies need to be set, you will get a comprehensible
        error.
        """
        mss = MemorySessionStore()
        router = Klein()

        @router.route("/")
        @inlineCallbacks
        def route(request: IRequest) -> Deferred:
            sproc = SessionProcurer(mss)
            request.write(b"oops...")
            with self.assertRaises(TooLateForCookies):
                yield sproc.procureSession(request)
            request.write(b"bye")
            request.finish()

        treq = StubTreq(router.resource())
        result = self.successResultOf(treq.get("http://unittest.example.com/"))
        self.assertEqual(self.successResultOf(result.content()), b"oops...bye")
Esempio n. 26
0
from twisted.web.template import slot, tags

from klein import Field, Form, Klein, Plating, Requirer, SessionProcurer
from klein.interfaces import ISession
from klein.storage.memory import MemorySessionStore

app = Klein()

sessions = MemorySessionStore()

requirer = Requirer()


@requirer.prerequisite([ISession])
def procurer(request):
    return SessionProcurer(sessions).procureSession(request)


style = Plating(tags=tags.html(tags.head(tags.title("yay")),
                               tags.body(tags.div(slot(Plating.CONTENT)))))


@requirer.require(
    style.routed(
        app.route("/", methods=["POST"]),
        tags.h1("u did it: ", slot("an-form-arg")),
    ),
    foo=Field.number(minimum=3, maximum=10),
    bar=Field.text(),
)
def postHandler(foo, bar):