Esempio n. 1
0
    def _login(self):
        self.getPage('/login', method='POST', body='login=admin&password=admin')

        cookie = BaseCookie()
        cookie.load(self.cookies[0][1])

        return cookie['session_id'].value
Esempio n. 2
0
    def unset_cookie(self, name):
        """Remove a cookie from those that are being sent with the response"""
        name = isomorphic_decode(name)
        cookies = self.headers.get("Set-Cookie")
        parser = BaseCookie()
        for cookie in cookies:
            parser.load(isomorphic_decode(cookie))

        if name in parser.keys():
            del self.headers["Set-Cookie"]
            for m in parser.values():
                if m.key != name:
                    self.headers.append(("Set-Cookie", m.OutputString()))
Esempio n. 3
0
    def logged_in(response, session_cookie_name):
        # This doesn't work because sessions are now created for guests.

        cookie_text = response.headers.get("set-cookie", None)
        if not cookie_text:
            return False
        cookies = BaseCookie()
        cookies.load(cookie_text)
        morsel = cookies.get(session_cookie_name, None)
        value = morsel and morsel.value

        # Cannot test to see if secure cookie can be parsed as JSON.
        if value:
            try:
                value = json.loads(value)
            except json.decoder.JSONDecodeError:
                print(
                    "Unable to parse value of cookie `%s` as JSON: %s." % (
                        session_cookie_name, repr(value)))

        return bool(value)
Esempio n. 4
0
    async def handleRequest(self, reader, writer):
        # connection id, for debugging
        connid = self.nextConnId
        self.nextConnId += 1

        logger.debug(f'{connid}: new incoming connection')
        # simple HTTP parsing, yes this is a terrible idea.
        l = await reader.readline()
        if l == bytes():
            raise BadRequest(f'{connid}: unexpected eof')
        l = l.rstrip(b'\r\n')
        try:
            method, rawPath, proto = l.split(b' ')
            logger.debug(f'{connid}: got {method} {rawPath} {proto}')
        except ValueError:
            logger.error(f'{connid}: cannot split line {l}')
            raise
        reqUrl = furl(rawPath.decode('utf-8'))

        headers = CIMultiDict()
        while True:
            if len(headers) > 100:
                raise BadRequest('too many headers')

            l = await reader.readline()
            if l == bytes():
                raise BadRequest(f'{connid}: unexpected eof in headers')
            l = l.rstrip(b'\r\n')
            logger.debug(f'{connid}: got header line {l!r}')
            # end of headers?
            if l == bytes():
                break
            try:
                key, value = l.decode('utf-8').split(':', 1)
                headers.add(key.strip(), value.strip())
            except ValueError:
                logger.error(f'cannot parse {l}')

        logger.debug(f'{connid}: {rawPath} {method} got headers {headers}')

        route = None
        try:
            netloc = headers['host']
            reqUrl = reqUrl.set(scheme='http', netloc=netloc)
            logger.debug(f'got request url {reqUrl}')
            routeKey = None
            for d in self.domain:
                m = parse(d, reqUrl.netloc)
                if m is not None:
                    routeKey = RouteKey(key=m['key'], user=m['user'])
                    logger.debug(f'{connid}: got route key {routeKey}')
                    break
            route = self.routes[routeKey]
        except (KeyError, ValueError):
            logger.info(f'{connid}: cannot find route for {reqUrl}')
            self.status['noroute'] += 1
            # error is written to client later

        # is this a non-forwarded request?
        segments = reqUrl.path.segments
        if len(segments) > 0 and segments[0] == '_conductor':
            if segments[1] == 'auth':
                logger.info(f'authorization request for {reqUrl.netloc}')
                try:
                    nextLoc = reqUrl.query.params['next'].encode('utf-8')
                except KeyError:
                    nextLoc = b'/'
                writer.write(b'\r\n'.join([
                    b'HTTP/1.0 302 Found', b'Location: ' + nextLoc,
                    b'Set-Cookie: authorization=' +
                    segments[2].encode('utf-8') + b'; HttpOnly; Path=/',
                    b'Cache-Control: no-store', b'',
                    b'Follow the white rabbit.'
                ]))
            elif segments[1] == 'status':
                writer.write(
                    b'HTTP/1.0 200 OK\r\nContent-Type: application/json\r\n\r\n'
                )
                self.status['routesTotal'] = len(self.routes)
                writer.write(
                    json.dumps(self.status, ensure_ascii=True).encode('ascii'))
            else:
                writer.write(
                    b'HTTP/1.0 404 Not Found\r\nContent-Type: plain/text\r\n\r\nNot found'
                )
            writer.close()
            return

        if not route:
            writer.write(
                b'HTTP/1.0 404 Not Found\r\nConnection: close\r\n\r\n')
            writer.close()
            return

        # check authorization
        cookies = BaseCookie()
        try:
            cookies.load(headers['Cookie'])
        except KeyError:
            # will be rejected later
            pass
        authorized = False
        for c in cookies.values():
            # Only hashed authorization is available to server.
            if c.key == 'authorization' and self.hashKey(
                    c.value) == route.auth:
                authorized = True
                break
        try:
            # do not forward auth cookie to the application, so it can’t leak it.
            del cookies['authorization']
            headers['Cookie'] = cookies.output(header='', sep='')
        except KeyError:
            # nonexistent cookie is fine
            pass

        if not authorized:
            logger.info(
                f'{connid}-{reqUrl}: not authorized, cookies sent {cookies.values()}'
            )
            writer.write(
                b'HTTP/1.0 403 Unauthorized\r\nContent-Type: plain/text\r\nConnection: close\r\n\r\nUnauthorized'
            )
            writer.close()
            self.status['unauthorized'] += 1
            return

        # try opening the socket
        try:
            start = time.time()
            sockreader, sockwriter = await asyncio.open_unix_connection(
                path=route.socket)
            end = time.time()
            logger.debug(f'opening socket took {end-start}s')
        except (ConnectionRefusedError, FileNotFoundError, PermissionError):
            logger.info(f'{connid}-{reqUrl}: route {routeKey} is broken')
            writer.write(
                b'HTTP/1.0 502 Bad Gateway\r\nConnection: close\r\n\r\n')
            writer.close()
            self.status['broken'] += 1
            return

        # some headers are fixed
        # not parsing body, so we cannot handle more than one request per connection
        # XXX: this is super-inefficient
        if 'Upgrade' not in headers or headers['Upgrade'].lower(
        ) != 'websocket':
            headers['Connection'] = 'close'

        # write http banner plus headers
        sockwriter.write(method + b' ' + rawPath + b' ' + proto + b'\r\n')
        for k, v in headers.items():
            sockwriter.write(f'{k}: {v}\r\n'.encode('utf-8'))
        sockwriter.write(b'\r\n')

        async def beforeABClose(result):
            if result == 0:
                # no response received from client
                logger.info(
                    f'{connid}-{reqUrl}: route {routeKey} got no result from server'
                )
                writer.write(
                    b'HTTP/1.0 502 Bad Gateway\r\nConnection: close\r\n\r\n')

        await proxy((sockreader, sockwriter, 'sock'), (reader, writer, 'web'),
                    logger=logger,
                    logPrefix=connid,
                    beforeABClose=beforeABClose)
Esempio n. 5
0
    def wsgi_app(self, environ, start_response):
        ctx = self.request_context(environ)
        ctx.push()

        req = ctx.request
        charset = req.charset

        session = ctx.session
        inner_ctx = ctx
        should_proxy = self.get_logged_in(session) 

        environ['flask_protector_app.verify_login'] = partial(self.verify_login, session=session)
        environ['flask_protector_app.set_logged_in'] = partial(self.set_logged_in, session)
        environ['flask_protector_app.get_logged_in'] = partial(self.get_logged_in, session)
        environ['flask_protector_app.get_logged_in_as'] = partial(self.get_logged_in_as, session)

        new_environ = environ.copy()

        if should_proxy:
            if 'HTTP_COOKIE' in new_environ:
                # Scrub the environment of any trace of the protector's cookie,
                # because otherwise the inner app will see it and probably try
                # to send Set-Cookie headers to refresh the session, effectively undoing
                # any changes the protector wants to make to it.
                
                parsed_cookie = BaseCookie()
                parsed_cookie.load(environ['HTTP_COOKIE']) # TODO encoding?
                del parsed_cookie[self.session_cookie_name]
                stringified_cookie = str(parsed_cookie).partition('Set-Cookie: ')[2]
                if stringified_cookie:
                    new_environ['HTTP_COOKIE'] = stringified_cookie
                else:
                    del new_environ['HTTP_COOKIE']

            inner_ctx = type(ctx)(
                self.wrapped_app,
                environ=new_environ,
                request=self.wrapped_app.request_class(new_environ)
            )
        
        error = None
        try:
            response = None
            try:
                if should_proxy:
                   inner_ctx.push()
                if not should_proxy:
                    response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:
                error = sys.exc_info()[1]
                raise
            if response is not None:
                return response(new_environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            if should_proxy:
                result = self.wrapped_app.wsgi_app(new_environ, start_response)
                inner_ctx.auto_pop(error)
                return result
            ctx.auto_pop(error)