Exemple #1
0
def get_falcon_api(
    broker_resource: BrokerResource,
    settings: Settings = Settings(),
    use_fallback_sessions: bool = False,
) -> API:
    logging.basicConfig(level=settings.logging.level.value)

    beaker_settings = {
        "session.type": settings.beaker.type,
        "session.cookie_expires": True,
        "session.auto":
        True,  # We got to have this on as things are currently programmed.
        "session.key": "JSESSIONID",
        "session.secure": True,
        "session.httponly": True,
        "session.data_dir": settings.beaker.data_dir,
    }
    beaker_middleware = None
    if not use_fallback_sessions:
        beaker_middleware = BeakerSessionMiddleware(beaker_settings)
    else:
        beaker_middleware = FallbackSessionMiddleware(beaker_settings)

    api = API(middleware=beaker_middleware,
              response_type=CookieCaseFixedResponse)
    api.add_route("/pcoip-broker/xml", resource=broker_resource)

    return api
class Server(BaseApplication):

    resources = {
        '/files': FilesResource(),
        '/file/{file_id}': FileResource(),
        '/file/{file_id}/mirrors': FileMirrorsResource(),
        '/file/{file_id}/mirror/{mirror_id}': FileMirrorResource(),
        '/file/{file_id}/mirror/{mirror_id}/reports': FileMirrorReportsResource()
    }

    def __init__(self, options=None):
        self.falcon = API()
        self.options = options or {}
        self.register_resources()
        super(Server, self).__init__()

    def load_config(self):
        config = {
            'bind': '{}:{}'.format(
                self.options.get('bind') or '127.0.0.1',
                self.options.get('port') or 8000)
        }
        for key, value in config.items():
            self.cfg.set(key, value)

    def load(self):
        return self.falcon

    def register_resources(self):
        for route, resource in self.resources.items():
            self.falcon.add_route(route, resource)
def test_caching_content_type(caches, eviction_strategy):
    """ Testing that the Content-Type header gets cached
    even when it is not the default, but it is set in the responder
    """
    # get the cache for the given eviction strategy
    cache = caches[eviction_strategy]

    # a resource where a custom response Cache-Type is set
    class CachedResource:
        @cache.cached(timeout=1)
        def on_get(self, req, resp):
            resp.content_type = 'mycustom/verycustom'
            if FALCONVERSION_MAIN < 3:
                resp.body = json.dumps({'num': random.randrange(0, 100000)})
            else:
                resp.text = json.dumps({'num': random.randrange(0, 100000)})

    app = API(middleware=cache.middleware)
    app.add_route('/randrange_cached', CachedResource())

    client = testing.TestClient(app)

    # the first call will cache it
    result1 = client.simulate_get('/randrange_cached')
    assert result1.headers['Content-Type'] == 'mycustom/verycustom'

    # the second call returns it from cache - but it still carries
    # the same content-type
    result2 = client.simulate_get('/randrange_cached')
    assert result1.json['num'] == result2.json['num']
    assert result2.headers['Content-Type'] == 'mycustom/verycustom'
def test_limit_by_resource_and_method():
    """ Test using a custom key_func - one which creates different buckets by resource and method
    """

    def get_key(req, resp, resource, params) -> str:
        user_key = get_remote_addr(req, resp, resource, params)
        return f"{user_key}:{resource.__class__.__name__}:{req.method}"

    limiter = Limiter(
        key_func=get_key,
        default_limits=["10 per hour", "1 per second"]
    )

    @limiter.limit()
    class ThingsResource:
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

        def on_post(self, req, resp):
            resp.body = 'Hello world!'

    app = API(middleware=limiter.middleware)
    app.add_route('/things', ThingsResource())

    client = testing.TestClient(app)
    r = client.simulate_get('/things')
    assert r.status == HTTP_200

    r = client.simulate_get('/things')
    assert r.status == HTTP_429

    # but a different endpoint can still be hit
    r = client.simulate_post('/things')
    assert r.status == HTTP_200
Exemple #5
0
def test_redis(redis_server):
    """ Test using the redis backend
    """

    limiter = Limiter(
        key_func=get_remote_addr,
        default_limits=["10 per hour", "1 per second"],
        config={
            'RATELIMIT_KEY_PREFIX': 'myapp',
            'RATELIMIT_STORAGE_URL': f'redis://@localhost:{REDIS_PORT}'
        }
    )

    @limiter.limit()
    class ThingsResource:
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

    app = API(middleware=limiter.middleware)
    app.add_route('/things', ThingsResource())

    client = testing.TestClient(app)
    r = client.simulate_get('/things')
    assert r.status == HTTP_200

    # due to the 1 second default limit on the default limiter
    r = client.simulate_get('/things')
    assert r.status == HTTP_429

    sleep(1)
    r = client.simulate_get('/things')
    assert r.status == HTTP_200
def test_get_remote_addr():
    """ Test using the get_remote_addr() key function as default
    """

    limiter = Limiter(
        key_func=get_remote_addr,
        default_limits=["10 per hour", "1 per second"]
    )

    @limiter.limit()
    class ThingsResource:
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

    app = API(middleware=limiter.middleware)
    app.add_route('/things', ThingsResource())

    client = testing.TestClient(app)
    r = client.simulate_get('/things')
    assert r.status == HTTP_200

    # due to the 1 second default limit on the default limiter
    r = client.simulate_get('/things')
    assert r.status == HTTP_429

    sleep(1)
    r = client.simulate_get('/things')
    assert r.status == HTTP_200
def test_default_limit(limiter):
    """ Test the default limit applied through the class decorator
    """
    @limiter.limit()
    class ThingsResource:
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

        def some_other_method(self):
            pass

    app = API(middleware=limiter.middleware)
    app.add_route('/things', ThingsResource())

    client = testing.TestClient(app)
    r = client.simulate_get('/things')
    assert r.status == HTTP_200

    # due to the 1 second default limit on the default limiter
    r = client.simulate_get('/things')
    assert r.status == HTTP_429

    sleep(1)
    r = client.simulate_get('/things')
    assert r.status == HTTP_200
Exemple #8
0
def add_routes(api: falcon.API, cfg: Config):
    """ Registers endpoints with Falcon
    Args:
        - api: Falcon API object
        - cfg: Configuration
    """
    api.add_route(HealthCheckResource.Path, HealthCheckResource())
def test_limit_on_method_overwrite(limiter):
    """ Test the limit decorator on the method overwriting the default limit
    """
    class ThingsResource:
        # the default limit on 'limiter' is 1 per second
        @limiter.limit(limits="2 per second")
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

    app = API(middleware=limiter.middleware)
    app.add_route('/things', ThingsResource())

    client = testing.TestClient(app)
    r = client.simulate_get('/things')
    assert r.status == HTTP_200

    r = client.simulate_get('/things')
    assert r.status == HTTP_200

    r = client.simulate_get('/things')
    assert r.status == HTTP_429

    sleep(1)
    r = client.simulate_get('/things')
    assert r.status == HTTP_200
def test_limit_class_and_method(limiter):
    """ Class level decorator gets overwritten by the method level one
    """
    # the default limit on 'limiter' is 1 per second
    @limiter.limit(limits="5 per hour;2 per second")
    class ThingsResource:
        # also the limits are in an unusual order:
        @limiter.limit(limits="3 per second;5 per hour")
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

    app = API(middleware=limiter.middleware)
    app.add_route('/things', ThingsResource())

    client = testing.TestClient(app)
    r = client.simulate_get('/things')
    assert r.status == HTTP_200

    r = client.simulate_get('/things')
    assert r.status == HTTP_200

    r = client.simulate_get('/things')
    assert r.status == HTTP_200

    r = client.simulate_get('/things')
    assert r.status == HTTP_429

    sleep(1)
    r = client.simulate_get('/things')
    assert r.status == HTTP_200
def test_limit_w_semicol(limiter):
    """ The limit might be provided semicol-separated
    """
    class ThingsResource:
        # the default limit on 'limiter' is 1 per second
        @limiter.limit(limits="5 per hour;2 per second")
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

    app = API(middleware=limiter.middleware)
    app.add_route('/things', ThingsResource())

    client = testing.TestClient(app)
    r = client.simulate_get('/things')
    assert r.status == HTTP_200

    r = client.simulate_get('/things')
    assert r.status == HTTP_200

    r = client.simulate_get('/things')
    assert r.status == HTTP_429

    sleep(1)
    r = client.simulate_get('/things')
    assert r.status == HTTP_200
Exemple #12
0
    def make_api(self):
        self.before_hooks.extend(self.hooks.optional_request_hooks())
        self.after_hooks.extend(self.hooks.optional_response_hooks())

        api = API(before=self.before_hooks, after=self.after_hooks)

        # Set the default route to the NYI object
        api.add_sink(self.default_route or NYI(before=self.before_hooks,
                                               after=self.after_hooks))

        # Add Error Handlers - ordered generic to more specific
        built_in_handlers = [(Exception, handle_unexpected_errors),
                             (ResponseException, ResponseException.handle),
                             (InvalidTokenError, InvalidTokenError.handle)]

        for ex, handler in built_in_handlers + self._error_handlers:
            api.add_error_handler(ex,
                                  wrap_handler_with_hooks(handler,
                                                          self.after_hooks))

        # Add all the routes collected thus far
        for _, disp in self._dispatchers.items():
            for endpoint, handler in disp.get_routes():
                LOG.debug("Loading endpoint %s", endpoint)
                api.add_route(endpoint, handler)
                api.add_route('%s.json' % endpoint, handler)

        return api
def test_dynamic_limits_on_method2():
    """ Test using the dynamic_limits param of the method decorators to change the limit per user

    Overwriting the default limits from the class level decortor
    """

    limiter = Limiter(key_func=get_remote_addr,
                      default_limits=["10 per hour", "1 per second"])

    @limiter.limit()
    class ThingsResource:
        @limiter.limit(dynamic_limits=lambda req, resp, resource,
                       req_succeeded: '5/second'
                       if req.get_header('APIUSER') == 'admin' else '2/second')
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

        def on_post(self, req, resp):
            resp.body = 'Hello world!'

    app = API(middleware=limiter.middleware)
    app.add_route('/things', ThingsResource())

    client = testing.TestClient(app)

    ####
    # 'normal' user - errors after more than 2 calls per sec
    r = client.simulate_get('/things')
    assert r.status == HTTP_200
    r = client.simulate_get('/things')
    assert r.status == HTTP_200

    # due to the 2/second limit
    r = client.simulate_get('/things')
    assert r.status == HTTP_429

    #########
    # 'admin' user should be able to make 5 calls
    admin_header = {"APIUSER": "******"}
    for i in range(5):
        r = client.simulate_get('/things', headers=admin_header)
        assert r.status == HTTP_200

    # at the 6th hit even the admin user will error:
    r = client.simulate_get('/things', headers=admin_header)
    assert r.status == HTTP_429

    sleep(1)
    r = client.simulate_get('/things', headers=admin_header)
    assert r.status == HTTP_200

    ########
    # the on_post() method gets the default limit - 1 per second
    r = client.simulate_post('/things', headers=admin_header)
    assert r.status == HTTP_200

    r = client.simulate_post('/things', headers=admin_header)
    assert r.status == HTTP_429
Exemple #14
0
def add_apis(app: falcon.API) -> None:
    for file in _CURRENT_DIR.glob('*.py'):
        if file.name.startswith('_'):
            continue

        importlib.import_module(f'.{file.stem}', 'regex')

    for klass in _BaseAPI.__subclasses__():
        uri_template = klass.__name__.lower().rstrip('api')
        app.add_route(f'/{uri_template}', klass())
Exemple #15
0
def create_client(uri_template, resource, handlers=None):
    app = API()
    app.add_route(uri_template, resource)

    if handlers:
        app.req_options.media_handlers.update(handlers)

    client = testing.TestClient(app)
    client.resource = resource

    return client
Exemple #16
0
def create_client(resource=None, middleware=None, handlers=None):
    res = resource or SimpleTestResource()

    app = API(middleware=middleware)
    app.add_route('/', res)

    if handlers:
        app.req_options.media_handlers.update(handlers)

    client = TestClient(app)
    client.resource = res

    return client
Exemple #17
0
def api(title, version):
    log = Log.get('api')

    middlewares = [Negotiation_Middleware()]

    if Arg_Reader.db.auth:
        log.notice('JWT authentication enabled')
        middlewares.append(
            Falcon_Auth_Middleware(
                JWT_Auth_Backend(
                    user_loader=lambda token: {'user': token},
                    secret_key=Arg_Reader.db.auth_secret_key,
                    auth_header_prefix=Arg_Reader.db.auth_header_prefix),
                exempt_routes=['/api/doc', '/api/doc/swagger.json']))
    else:
        log.notice('JWT authentication disabled')

    if Arg_Reader.db.https:
        log.notice('Force to use HTTPS instead of HTTP')
        middlewares.append(RequireHTTPS())
    else:
        log.notice('HTTPS not set')

    if Arg_Reader.db.apm_enabled:
        log.notice('Elastic APM enabled')
        middlewares.append(
            Elastic_Apm_Middleware(service_name='lcp-apm',
                                   server_url=Arg_Reader.db.apm_server))
    else:
        log.notice('Elastic APM disabled')

    instance = API(middleware=middlewares)

    media_handlers = {
        falcon.MEDIA_JSON:
        JSON_Handler(loads=loads,
                     dumps=partial(dumps, ensure_ascii=False, sort_keys=True)),
        falcon.MEDIA_MSGPACK:
        Message_Pack_Handler(),
        falcon.MEDIA_XML:
        XML_Handler(),
        falcon.MEDIA_YAML:
        YAML_Handler()
    }
    instance.req_options.media_handlers.update(media_handlers)
    instance.resp_options.media_handlers.update(media_handlers)

    instance.add_error_handler(*Bad_Request_Handler.get())
    instance.add_error_handler(*Internal_Server_Error_Handler.get())
    instance.add_error_handler(*Unsupported_Media_Type_Handler.get())

    api_spec = Spec(api=instance, title=title, version=version)
    routes(api=instance, spec=api_spec.get())
    falcon_api_doc(instance,
                   config_path='./swagger/schema.json',
                   url_prefix='/api/doc',
                   title='API doc')
    api_spec.write()

    return instance
def register_routes(falcon_api: API, get_current_model: GetModel):
    falcon_api.add_route('/', SwaggerController())
    falcon_api.add_route('/api/hc', MonitorController())
    falcon_api.add_route(
        '/api/mlmodel',
        DummyModelController(get_current_model=get_current_model))
    falcon_api.add_static_route('/static', STATIC_APPLICATION_PATH)
Exemple #19
0
    def setUp(self):
        super().setUp()
        self.api = API(middleware=self.auth_middleware)
        self.api.add_route(self.route, ExampleResource())

        self.auth_storage.clear()

        identity = [self.user[key] for key in self.ident_keys]

        self.auth_storage.register(
            self.auth_middleware[0],
            identity[0] if len(identity) == 1 else identity,
            self.user
        )
Exemple #20
0
def set_routes(api: API, db: Session) -> API:
    user = UserResource(db)
    api.add_route('/statz', Statz())
    api.add_route('/users/{user_id}', user)
    api.add_route('/users', user)
    api.add_route('/login', LoginResource(db))
    return api
Exemple #21
0
def add_api_routes(app: falcon.API, runner: JobRunner):
    logger.info('Adding routes to api')

    app.add_route('/submit', SubmitJobResource(runner))
    app.add_route('/status/{job_id}', JobStatusResource(runner))
    app.add_route('/result/{job_id}', ClientCallbackResource(runner))
    app.add_route('/test', TestingEndpoint())
    logger.info('All routes added')
    def test_empty_requests_dropped(self, empty_body, endpoint,
                                    endpoint_exists):
        # type: (bool, str, bool) -> None
        """Test that empty requests are dropped before routing

        Note that content_length is None if not explicitly set,
        so HEAD, GET, etc. requests are fine.
        """
        client = testing.TestClient(API(middleware=[m.EmptyRequestDropper()]))

        # We have to forge the content-length header, because if it is
        # made automatically for our empty body, we won't get to the check
        # of the body contents.
        heads = {"Content-Length": str("20")} if empty_body else {}
        body = "" if empty_body else None

        res = client.simulate_get(endpoint, body=body,
                                  headers=heads)  # type: testing.Result

        if empty_body:
            assert res.status == status_codes.HTTP_BAD_REQUEST
        else:
            if endpoint_exists:
                assert bool(
                    200 <= res.status_code < 300
                    or res.status_code == status_codes.HTTP_METHOD_NOT_ALLOWED)
            else:
                assert res.status == status_codes.HTTP_NOT_FOUND
def test_undefined_endpoint(limiter):
    """ Test calling a method which is not defined at all

    Our module should not error, but we should leave it to Falcon to handle it - and return a 405.
    """
    @limiter.limit()
    class ThingsResource:
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

    app = API(middleware=limiter.middleware)
    app.add_route('/things', ThingsResource())

    client = testing.TestClient(app)
    r = client.simulate_post('/things')
    assert r.status == HTTP_405
Exemple #24
0
def autowire_resources(api: falcon.API,
                       spec: Specification,
                       resources: list,
                       ignore_errors: bool = False) -> None:
    """Resources must be annotated with @tag(path=)
    You can inspect each operation's ``handler`` attribute to find the routed function.

    .. code-block: python

        >>> @tag(path="/users{userId}/widgets")
        ... class WidgetCollectionResource:
        ...     def on_get(self): ...
    """
    def fail(msg: str) -> None:
        if ignore_errors:
            logger.warning(msg)
        else:
            raise RuntimeError(msg)

    for resource in resources:
        cls = resource.__class__
        path = get_tag("path", resource)
        if not path:
            fail(f"{cls} does not have an @tag(path=) annotation")
            continue
        defined_operations = spec.operations.with_path(path)
        if not defined_operations:
            fail(f"{cls}@{path} has no known operations")
        has_operations = {
            o
            for o in defined_operations if hasattr(resource, f"on_{o.verb}")
        }  # type: Set[Operation]
        missing_operations = [o for o in (defined_operations - has_operations)]
        if missing_operations:
            missing_operations.sort(key=lambda o: o.id)
            fail(f"{cls}@{path} is missing the following handlers:" +
                 "".join(f"\n    {o.id}::on_{o.verb}"
                         for o in missing_operations))
        api.add_route(path, resource)
        for o in has_operations:
            logger.info(f"added route for {o.id} {o.verb} {o.path}")
            o.handler = getattr(resource, f"on_{o.verb}")
    missing_operations = [o.id for o in spec.operations if not o.handler]
    if missing_operations:
        missing_operations.sort()
        fail("The following operations do not have handlers:" +
             "".join(f"\n    {o}" for o in missing_operations))
Exemple #25
0
def test_simulate_request_http_version(version, valid):
    app = API()

    if valid:
        testing.simulate_request(app, http_version=version)
    else:
        with pytest.raises(ValueError):
            testing.simulate_request(app, http_version=version)
def test_get_remote_addr(limiter):
    """ Test the utils.get_remote_addr() function which returns the requestor's ip

    Create an app with the default limiter and a method which is calling the get_remote_addr()
    """
    @limiter.limit()
    class ThingsResource:
        def on_get(self, req, resp):
            resp.body = 'Hello world!'
            assert '127.0.0.1' == get_remote_addr(req, resp, None, None)

    app = API(middleware=limiter.middleware)
    things = ThingsResource()
    app.add_route('/things', things)

    client = testing.TestClient(app)
    client.simulate_get('/things')
Exemple #27
0
def add_routes(api: falcon.API):
    storage = DataStorage()
    job_queue = deque()
    start_worker(storage, job_queue)

    api.add_route("/", SegmentationBase())
    api.add_route("/docs", SegmentationDocs())
    # Note: falcon internally strips trailing slashes from requests
    api.add_route("/jobs", SegmentationJobs(storage, job_queue))
    api.add_route("/jobs/{job_id}", SegmentationJob(storage))
Exemple #28
0
def serve(port: int = 3000, root_dir: Opt[str] = None) -> None:
    '''
    Start a server providing access to the records in a directory.
    '''
    root_dir = Path(root_dir or get_conf().root_dir)

    def write_response(req: Request, res: Response) -> None:
        res.content_type = 'application/cbor'
        res.set_header('Access-Control-Allow-Origin', '*')

        if req.path.endswith('/_entry-names'):
            path = root_dir / req.path[1:-len('/_entry-names')]
            if path.is_file(): raise HTTPStatus(HTTP_404)
            res.data = cbor2.dumps(dict(
                type='plain-object',
                content=sorted([
                    re.sub(r'\.h5$', '', p.name) + ('/' if p.is_dir() else '')
                    for p in path.glob('[!_]*')
                ])
            ))

        elif req.path.endswith('/_meta'):
            key = req.path[1:-len('/_meta')]
            res.data = cbor2.dumps(_read_meta(root_dir, key))

        else:
            t_last = float(req.get_param('t_last') or 0) / 1000
            entry = _read(root_dir, req.path[1:], t_last)
            if entry['type'] == 'file':
                res.data = (root_dir / entry['content']).read_bytes()
            else:
                res.data = cbor2.dumps(entry)

        res.status = HTTP_200

    app = API(middleware=[_HandleCORS()])
    app.add_sink(write_response)

    class Server(GunicornApp): # type: ignore
        def load(self) -> API:
            return app
        def load_config(self) -> None:
            self.cfg.set('bind', f'localhost:{port}')
            self.cfg.set('workers', 2 * cpu_count())
    Server().run()
def test_different_key_prefixes():
    """ Test using different key_prefixes
    """

    limiter1 = Limiter(
        key_func=get_remote_addr,
        default_limits=["10 per hour", "1 per second"],
        config={
            'RATELIMIT_KEY_PREFIX': 'app1'
        }
    )

    @limiter1.limit()
    class ThingsResource:
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

    app1 = API(middleware=limiter1.middleware)
    app1.add_route('/things', ThingsResource())

    client1 = testing.TestClient(app1)
    r = client1.simulate_get('/things')
    assert r.status == HTTP_200

    # due to the 1 second default limit on the default limiter
    r = client1.simulate_get('/things')
    assert r.status == HTTP_429


    #####
    # app2 - with a different key

    limiter2 = Limiter(
        key_func=get_remote_addr,
        default_limits=["10 per hour", "1 per second"],
        config={
            'RATELIMIT_KEY_PREFIX': 'app2'
        }
    )

    @limiter2.limit()
    class ThingsResource:
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

    app2 = API(middleware=limiter2.middleware)
    app2.add_route('/things', ThingsResource())

    client2 = testing.TestClient(app2)
    r = client2.simulate_get('/things')
    assert r.status == HTTP_200

    # due to the 1 second default limit on the default limiter
    r = client2.simulate_get('/things')
    assert r.status == HTTP_429
def test_default_dynamic_limits():
    """ Test using the default_dynamic_limits option to change the limit per user
    """

    limiter = Limiter(key_func=get_remote_addr,
                      default_dynamic_limits=lambda req, resp, resource,
                      req_succeeded: '5/second'
                      if req.get_header('APIUSER') == 'admin' else '2/second')

    @limiter.limit()
    class ThingsResource:
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

    app = API(middleware=limiter.middleware)
    app.add_route('/things', ThingsResource())

    client = testing.TestClient(app)

    ####
    # 'normal' user - errors after more than 2 calls per sec
    r = client.simulate_get('/things')
    assert r.status == HTTP_200
    r = client.simulate_get('/things')
    assert r.status == HTTP_200

    # due to the 2/second limit
    r = client.simulate_get('/things')
    assert r.status == HTTP_429

    #########
    # 'admin' user should be able to make 5 calls
    admin_header = {"APIUSER": "******"}
    for i in range(5):
        r = client.simulate_get('/things', headers=admin_header)
        assert r.status == HTTP_200

    # at the 6th hit even the admin user will error:
    r = client.simulate_get('/things', headers=admin_header)
    assert r.status == HTTP_429

    sleep(1)
    r = client.simulate_get('/things', headers=admin_header)
    assert r.status == HTTP_200
    def test_content_type_json_required(self, endpoint, method,
                                        endpoint_exists, ct_is_json, required):
        # type: (str, str, bool, bool, bool) -> None
        """Test HTTP methods that should reuquire content-type json

        Note ``required`` is always true at the moment, because the
        middleware applies to all requests with no exceptions. If
        that changes, add parameters above.

        The endpoint here does not really matter, because
        ``process_request`` middleware, which handles enforcement
        of the accept header, is processed before routing. However,
        the ``endpoint_exists`` option is provided in case it is
        necessary in the future to ensure this is called against
        a real endpoint.
        """
        client = testing.TestClient(API(middleware=[m.JSONEnforcer()]))

        if ct_is_json:
            heads = {'Content-Type': str('application/json')}
        else:
            heads = {}

        res = client.simulate_request(
            method=str(method), path=endpoint, headers=heads
        )  # type: testing.Result

        meth_not_allowed = status_codes.HTTP_METHOD_NOT_ALLOWED
        if required:
            if method in m.JSON_CONTENT_REQUIRED_METHODS:
                # Ensure that non-json content type is denied
                if ct_is_json:
                    if endpoint_exists:
                        assert bool(
                            200 <= res.status_code < 300 or
                            res.status_code == meth_not_allowed
                        )
                    else:
                        assert res.status == status_codes.HTTP_NOT_FOUND
                else:
                    exp_code = status_codes.HTTP_UNSUPPORTED_MEDIA_TYPE
                    assert res.status == exp_code
            else:
                # We shouldn't deny for non-json content type
                if endpoint_exists:
                    assert bool(
                        200 <= res.status_code < 300 or
                        res.status_code == meth_not_allowed
                    )
                else:
                    assert res.status == status_codes.HTTP_NOT_FOUND
        else:
            # No endpoints for which this is applicable exist at the moment
            pass
Exemple #32
0
class AuthTestsMixin:
    """ Test mixin that defines common routine for testing auth classes.
    """

    class SkipTest(Exception):
        """Raised when given tests is marked to be skipped

        Note: we use this exception instead of self.skipTest() method because
        this has slightly different semantics. We simply don't want to report
        these tests as skipped.
        """

    route = '/foo/'
    user = {
        "username": "******",
        "details": "bar",
        "password": "******",
        "allowed_ip": "127.100.100.1",
        "allowed_remote": "127.0.0.1",
        "token": "s3cr3t70ken",
        'allowed_ip_range': ['127.100.100.1'],
    }
    ident_keys = ['password']
    auth_storage = ExampleKVUserStorage()
    auth_middleware = [authentication.Anonymous(user)]

    def get_authorized_headers(self):
        raise NotImplementedError

    def get_invalid_headers(self):
        raise NotImplementedError

    def get_unauthorized_headers(self):
        return {}

    def setUp(self):
        super().setUp()
        self.api = API(middleware=self.auth_middleware)
        self.api.add_route(self.route, ExampleResource())

        self.auth_storage.clear()

        identity = [self.user[key] for key in self.ident_keys]

        self.auth_storage.register(
            self.auth_middleware[0],
            identity[0] if len(identity) == 1 else identity,
            self.user
        )

    def test_unauthorized(self):
        try:
            self.simulate_request(
                self.route, decode='utf-8', method='GET',
                headers=self.get_unauthorized_headers()
            )
            assert self.srmock.status == status_codes.HTTP_UNAUTHORIZED
        except self.SkipTest:
            pass

    def test_authorized(self):
        try:
            self.simulate_request(
                self.route, decode='utf-8', method='GET',
                headers=self.get_authorized_headers()
            )

            assert self.srmock.status == status_codes.HTTP_OK
        except self.SkipTest:
            pass

    def test_bad_request(self):
        try:
            maybe_multiple_headers_sets = self.get_invalid_headers()

            if isinstance(maybe_multiple_headers_sets, tuple):
                header_sets = maybe_multiple_headers_sets

            else:
                header_sets = (maybe_multiple_headers_sets,)

            for headers in header_sets:
                self.simulate_request(
                    self.route, decode='utf-8', method='GET',
                    headers=headers
                )
                assert self.srmock.status == status_codes.HTTP_BAD_REQUEST
        except self.SkipTest:
            pass
Exemple #33
0
class FalconAdapter(Adapter):

    def __init__(self, *args, **kwargs):
        super(FalconAdapter, self).__init__(*args, **kwargs)
        self.falcon_api = FalconAPI()
        # Add Falcon-specific URI routes
        for uri, resource in self.get_embedded_rules():
            self.falcon_api.add_route(uri, resource)

    def adapt_handler(self, handler, resource, request,
                      response, *args, **kwargs):
        """Adapt the request object and the response object for
        the `handler` function.

        :param handler: the handler function to be adapted.
        :param resource: the Falcon resource object.
        :param request: the Falcon request object.
        :param response: the Falcon response object.
        :param args: a list of positional arguments that will be passed
                     to the handler.
        :param kwargs: a dictionary of keyword arguments that will be passed
                       to the handler.
        """
        adapted_request = FalconRequest(request)
        api_response = handler(adapted_request, *args, **kwargs)

        # Convert the RESTArt response object to a Falcon response object
        response.body = api_response.data
        response.status = api_response.status
        response.set_headers(api_response.headers)

    def wsgi_app(self, environ, start_response):
        """The actual Falcon-specific WSGI application.

        See :meth:`~restart.serving.Service.wsgi_app` for the
        meanings of the parameters.
        """
        return self.falcon_api(environ, start_response)

    def get_embedded_rules(self):
        """Get the Falcon-specific rules used to be embedded into
        an existing or legacy application.

        Example:

            # The existing Falcon API
            import falcon
            app = falcon.API()
            ...

            # The RESTArt API
            from restart.api import RESTArt
            api = RESTArt()
            ...

            # Embed RESTArt into Falcon
            from restart.serving import Service
            from restart.ext.falcon.adapter import FalconAdapter
            service = Service(api, FalconAdapter)
            for uri, resource in service.embedded_rules:
                app.add_route(uri, resource)
        """
        rules = []
        for endpoint, rule in iteritems(self.adapted_rules):
            resource = FalconResource()
            for method in rule.methods:
                on_method = types.MethodType(rule.handler, resource)
                setattr(resource, 'on_' + method.lower(), on_method)
            # Convert URI style from RESTArt's `<arg>` to Falcon's `{arg}`
            uri = rule.uri.replace('<', '{').replace('>', '}')
            assert ':' not in uri, (
                'Werkzeug-style converters (with the separator ":") is '
                'not supported in `RESTArt-Falcon`. Captured parameters '
                'in URI can only be specified in the form of "<arg>"'
            )
            rules.append((uri, resource))
        return tuple(rules)
Exemple #34
0
 def __init__(self, *args, **kwargs):
     super(FalconAdapter, self).__init__(*args, **kwargs)
     self.falcon_api = FalconAPI()
     # Add Falcon-specific URI routes
     for uri, resource in self.get_embedded_rules():
         self.falcon_api.add_route(uri, resource)
import six

from falcon import API
from falcon.cmd import print_routes
from falcon.testing import redirected


class DummyResource(object):

    def on_get(self, req, resp):
        resp.body = 'Test\n'
        resp.status = '200 OK'


_api = API()
_api.add_route('/test', DummyResource())


def test_traverse_with_verbose():
    """Ensure traverse() finds the proper routes and outputs verbose info."""

    output = six.moves.StringIO()
    with redirected(stdout=output):
        print_routes.traverse(_api._router._roots, verbose=True)

    route, get_info, options_info = output.getvalue().strip().split('\n')
    assert '-> /test' == route

    # NOTE(kgriffs) We might receive these in either order, since the
    # method map is not ordered, so check and swap if necessary.
    if options_info.startswith('-->GET'):
Exemple #36
0
from falcon import API
import endpoints
import pymongo

class CorsMiddleware(object):
    def process_request(self, request, response):
        response.set_header('Access-Control-Allow-Origin', '*')

db = pymongo.MongoClient('mongodb.docker')
app = API(middleware=[CorsMiddleware()])

app.add_route('/', endpoints.BaseResponse(db))
app.add_route('/status', endpoints.ProjectStatus(db))
app.add_route('/status/{status_id}', endpoints.ProjectDetail(db))

import sys

import testtools

try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO

from falcon import API
from falcon.cmd import print_routes


_api = API()
_api.add_route('/test', None)

STDOUT = sys.stdout


class TestPrintRoutes(testtools.TestCase):

    def setUp(self):
        """Capture stdout"""
        super(TestPrintRoutes, self).setUp()
        self.output = StringIO()
        sys.stdout = self.output

    def tearDown(self):
        """Reset stdout"""
        super(TestPrintRoutes, self).tearDown()
        self.output.close()
Exemple #38
0
def client():

    api = API(middleware=ValidatorComponent())
    api.add_route(SONGFINISH_ROUTE, SongFinish(token=TOKEN))

    return testing.TestClient(api)
Exemple #39
0
 def __init__(*args, **kwargs):
     if 'logger' in kwargs: #FIXME: add check to see if subclass() of logger.*some_logger*
         args[0]._logger = kwargs['logger']
         del kwargs['logger']
     Falcon_API.__init__(*args, **kwargs)
 def __init__(self, options=None):
     self.falcon = API()
     self.options = options or {}
     self.register_resources()
     super(Server, self).__init__()