예제 #1
0
    def device_register(self, path=None):
        """
        EXPECTING A SIGNED REGISTRATION REQUEST
        RETURN JSON WITH url FOR LOGIN
        """
        now = Date.now()
        expires = now + parse(self.device.register.session['max-age'])
        request_body = request.get_data()
        signed = json2value(request_body.decode("utf8"))
        command = json2value(base642bytes(signed.data).decode("utf8"))
        session.public_key = command.public_key
        rsa_crypto.verify(signed, session.public_key)

        self.session_manager.create_session(session)
        session.expires = expires.unix
        session.state = bytes2base64URL(crypto.bytes(32))

        with self.device.db.transaction() as t:
            t.execute(
                sql_insert(
                    self.device.table,
                    {
                        "state": session.state,
                        "session_id": session.session_id
                    },
                ))
        body = value2json(
            Data(
                session_id=session.session_id,
                interval="5second",
                expires=session.expires,
                url=URL(
                    self.device.home,
                    path=self.device.endpoints.login,
                    query={"state": session.state},
                ),
            ))

        response = Response(body,
                            headers={"Content-Type": mimetype.JSON},
                            status=200)
        response.set_cookie(self.device.register.session.name,
                            session.session_id,
                            path=self.device.login.session.path,
                            domain=self.device.login.session.domain,
                            expires=expires.format(RFC1123),
                            secure=self.device.login.session.secure,
                            httponly=self.device.login.session.httponly)

        return response
예제 #2
0
    def device_register(self, path=None):
        """
        EXPECTING A SIGNED REGISTRATION REQUEST
        RETURN JSON WITH url FOR LOGIN
        """
        now = Date.now().unix
        request_body = request.get_data().strip()
        signed = json2value(request_body.decode("utf8"))
        command = json2value(base642bytes(signed.data).decode("utf8"))
        session.public_key = command.public_key
        rsa_crypto.verify(signed, session.public_key)

        self.session_manager.setup_session(session)
        session.expires = now + parse("10minute").seconds
        session.state = bytes2base64URL(Random.bytes(32))

        with self.device.db.transaction() as t:
            t.execute(
                sql_insert(
                    self.device.table,
                    {
                        "state": session.state,
                        "session_id": session.session_id
                    },
                ))
        response = value2json(
            Data(
                session_id=session.session_id,
                interval="5second",
                expiry=session.expires,
                url=URL(
                    self.device.home,
                    path=self.device.endpoints.login,
                    query={"state": session.state},
                ),
            ))

        return Response(response,
                        headers={"Content-Type": "application/json"},
                        status=200)
예제 #3
0
    def device_status(self, path=None):
        """
        AUTOMATION CAN CALL THIS ENDPOINT TO FIND OUT THE LOGIN STATUS
        RESPOND WITH {"ok":true} WHEN USER HAS LOGGED IN, AND user IS
        ASSOCIATED WITH SESSION
        """
        now = Date.now().unix
        session_id = request.cookies.get(self.device.register.session.name)
        if not session_id:
            return Response('{"try_again":false, "status":"no session id"}',
                            status=401)
        device_session = self.session_manager.get_session(session_id)

        request_body = request.get_data()
        signed = json2value(request_body.decode("utf8"))
        command = rsa_crypto.verify(signed, device_session.public_key)

        time_sent = parse(command.timestamp).unix
        if not (now - LEEWAY <= time_sent < now + LEEWAY):
            return Response(
                '{"try_again":false, "status":"timestamp is not recent"}',
                status=401)
        if device_session.expires < now:
            return Response(
                '{"try_again":false, "status":"session is too old"}',
                status=401)
        if device_session.user:
            device_session.public_key = None
            return Response('{"try_again":false, "status":"verified"}',
                            status=200)

        state_info = self.device.db.query(
            sql_query({
                "select": "session_id",
                "from": self.device.table,
                "where": {
                    "eq": {
                        "state": device_session.state
                    }
                },
            }))
        if not state_info.data:
            return Response(
                '{"try_again":false, "status":"State has been lost"}',
                status=401)

        return Response('{"try_again":true, "status":"still waiting"}',
                        status=200)
예제 #4
0
    def login(self, please_stop=None):
        """
        WILL REGISTER THIS DEVICE, AND SHOW A QR-CODE TO LOGIN
        WILL POLL THE SERVICE ENDPOINT UNTIL LOGIN IS COMPLETED, OR FAILED

        :param please_stop: SIGNAL TO STOP EARLY
        :return: SESSION THAT CAN BE USED TO SEND AUTHENTICATED REQUESTS
        """
        # SEND PUBLIC KEY
        now = Date.now().unix
        self.session = requests.Session()
        signed = rsa_crypto.sign(
            Data(public_key=self.public_key, timestamp=now), self.private_key)
        DEBUG and Log.note("register (unsigned)\n{{request|json}}",
                           request=rsa_crypto.verify(signed, self.public_key))
        DEBUG and Log.note("register (signed)\n{{request|json}}",
                           request=signed)
        try:
            response = self.session.request(
                "POST",
                str(URL(self.config.service) / self.config.endpoints.register),
                data=value2json(signed))
        except Exception as e:
            raise Log.error("problem registering device", cause=e)

        device = wrap(response.json())
        DEBUG and Log.note("response:\n{{response}}", response=device)
        device.interval = parse(device.interval).seconds
        expires = Till(till=parse(device.expiry).unix)
        cookie = self.session.cookies.get(self.config.cookie.name)
        if not cookie:
            Log.error("expecting a session cookie")

        # SHOW URL AS QR CODE
        image = text2QRCode(device.url)

        sys.stdout.write("\n\nLogin using thie URL:\n")
        sys.stdout.write(device.url + CR)
        sys.stdout.write(image)

        while not please_stop and not expires:
            Log.note("waiting for login...")
            try:
                now = Date.now()
                signed = rsa_crypto.sign(Data(timestamp=now, session=cookie),
                                         self.private_key)
                url = URL(self.config.service) / self.config.endpoints.status
                DEBUG and Log.note("ping (unsigned) {{url}}\n{{request|json}}",
                                   url=url,
                                   request=rsa_crypto.verify(
                                       signed, self.public_key))
                response = self.session.request("POST",
                                                url,
                                                data=value2json(signed))
                ping = wrap(response.json())
                DEBUG and Log.note("response\n{{response|json}}",
                                   response=ping)
                if ping.status == "verified":
                    return self.session
                if not ping.try_again:
                    Log.note("Failed to login {{reason}}", reason=ping.status)
                    return
            except Exception as e:
                Log.warning(
                    "problem calling {{url}}",
                    url=URL(self.config.service) /
                    self.config.endpoints.status,
                    cause=e,
                )
            (Till(seconds=device.interval) | please_stop | expires).wait()
        return self.session