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
Example #2
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_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
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
Example #5
0
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_no_limit(limiter):
    """ Test a no limit resource even when another resource has a limit
    """
    @limiter.limit()
    class ThingsResource:
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

    class ThingsResourceNoLimit:
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

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

    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

    for i in range(3):
        r = client.simulate_get('/thingsnolimit')
        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
Example #8
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_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
def hydrated_client_multiple_middleware():
    """A Falcon API with an endpoint for testing Marshmallow, with all
    included middlewares registered."""
    data_store = DataStore()

    class PhilosopherResource:

        schema = Philosopher()

        def on_get(self, req, resp, phil_id):
            """Get a philosopher"""
            req.context['result'] = data_store.get(phil_id)

    class PhilosopherCollection:

        schema = Philosopher()

        def on_post(self, req, resp):
            req.context['result'] = data_store.insert(req.context['json'])

    app = API(middleware=[
        m.Marshmallow(),
        m.JSONEnforcer(),
        m.EmptyRequestDropper()])

    app.add_route('/philosophers', PhilosopherCollection())
    app.add_route('/philosophers/{phil_id}', PhilosopherResource())

    yield testing.TestClient(app)

    data_store.clear()
Example #12
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
Example #13
0
def app(request, limiter):
    """ Creates a Falcon app with the default limiter
    """
    @limiter.limit()
    class ThingsResource:
        # unmarked methods will use the default limit
        def on_get(self, req, resp):
            resp.body = 'Hello world!'

        # mark this method with a special limit
        # which will overwrite the default
        @limiter.limit(limits="1 per day")
        def on_post(self, req, resp):
            pass

    # a resource with no limits:
    class ThingsResourceNoLimit:
        # unmarked methods will use the default limit
        def on_get(self, req, resp):
            pass

    # add the limiter middleware to the Falcon app
    app = API(middleware=limiter.middleware)

    things = ThingsResource()
    thingsnolimit = ThingsResourceNoLimit()

    app.add_route('/things', things)
    app.add_route('/thingsnolimit', thingsnolimit)

    return app
Example #14
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 hydrated_client_multiple_middleware():
    """Create a server for testing, with all included middlewares"""

    data_store = DataStore()

    class PhilosopherResource:

        schema = Philosopher()

        def on_get(self, req, resp, phil_id):
            """Get a philosopher"""
            req.context["result"] = data_store.get(phil_id)

    class PhilosopherCollection:

        schema = Philosopher()

        def on_post(self, req, resp):
            req.context["result"] = data_store.insert(req.context["json"])

    app = API(middleware=[
        m.Marshmallow(),
        m.JSONEnforcer(),
        m.EmptyRequestDropper()
    ])

    app.add_route("/philosophers", PhilosopherCollection())
    app.add_route("/philosophers/{phil_id}", PhilosopherResource())

    yield testing.TestClient(app)

    data_store.clear()
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 hydrated_client():
    """Create a Falcon API with an endpoint for testing Marshmallow"""
    data_store = DataStore()

    class PhilosopherResource:

        schema = Philosopher()

        def on_get(self, req, resp, phil_id):
            """Get a philosopher"""
            req.context["result"] = data_store.get(phil_id)

    class PhilosopherCollection:

        schema = Philosopher()

        def on_post(self, req, resp):
            req.context["result"] = data_store.insert(req.context["json"])

    app = API(middleware=[m.Marshmallow()])

    app.add_route("/philosophers", PhilosopherCollection())
    app.add_route("/philosophers/{phil_id}", PhilosopherResource())

    yield testing.TestClient(app)

    data_store.clear()
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
def test_caching_multiple_decorators_on_method(caches, eviction_strategy):
    """ Testing caching when there are multiple decorators on the method
    """
    # get the cache for the given eviction strategy
    cache = caches[eviction_strategy]

    class CachedResource:
        # a resource where the cache is the first decorator
        @cache.cached(timeout=1)
        @a_decorator
        def on_get(self, req, resp):
            if FALCONVERSION_MAIN < 3:
                resp.body = json.dumps({'num': random.randrange(0, 100000)})
            else:
                resp.text = json.dumps({'num': random.randrange(0, 100000)})

    class CachedResource2:
        # a resource where the cache is NOT the first decorator
        @a_decorator
        @cache.cached(timeout=1)
        def on_get(self, req, resp):
            if FALCONVERSION_MAIN < 3:
                resp.body = json.dumps({'num': random.randrange(0, 100000)})
            else:
                resp.text = json.dumps({'num': random.randrange(0, 100000)})

    class CachedResource3:
        # a resource where the cache is NOT the first decorator, but the register() is used
        @register(a_decorator, cache.cached(timeout=1))
        def on_get(self, req, resp):
            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())
    app.add_route('/randrange_cached2', CachedResource2())
    app.add_route('/randrange_cached3', CachedResource3())

    client = testing.TestClient(app)

    # scenario 1 - the cache is the first decorator
    result1 = client.simulate_get('/randrange_cached')
    result2 = client.simulate_get('/randrange_cached')
    assert result1.json['num'] == result2.json['num']

    # scenario 2 - the cache is NOT the first decorator - caching does NOT work!
    result1 = client.simulate_get('/randrange_cached2')
    result2 = client.simulate_get('/randrange_cached2')
    assert result1.json['num'] != result2.json['num']

    # scenario 3 - the cache is the NOT first decorator, but register() is used, so caching does work!
    result1 = client.simulate_get('/randrange_cached3')
    result2 = client.simulate_get('/randrange_cached3')
    assert result1.json['num'] == result2.json['num']
Example #20
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())
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
Example #22
0
def create_api(model_container=None):
    api = API()

    if not model_container:
        # model_container instance
        model_container = ModelContainer()

    # handling requests
    api.add_route('/predict', PredictController(model_container))
    api.add_route('/info', InfoController())
    return api
Example #23
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
Example #24
0
def app(environment=None):
    """
    Create our echo falcon app.
    """
    if environment is None:
        environment = os.environ
    logging.basicConfig()
    logging.getLogger("falcon").setLevel(logging.DEBUG)
    api = API()
    api.add_route("/ping", PingResource())
    api.add_sink(sink(environment))
    return api
Example #25
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
Example #26
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
Example #27
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')
Example #28
0
def main():
    global app
    specfile = "./apis/api.yaml"
    specurl = "file://" + abspath(specfile)
    specdict = read_yaml_file(specfile)
    openapi_spec = create_spec(specdict, spec_url=specurl)

    openapi_middleware = FalconOpenAPIMiddleware.from_spec(openapi_spec)

    app = API(middleware=[openapi_middleware])

    auth_server = Auth()
    user_server = User()

    app.add_route('/user', user_server)
    app.add_route('/token', auth_server)
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
Example #30
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))
Example #31
0
def main():
    if not docker_is_available():
        print("Docker does not seem to be available..")
        return 1
    docker_client = Docker()
    languages_handler = Languages(docker_client)
    code_exec_handler = CodeExec(docker_client)

    app = API()
    app.add_route("/exec/{lang}", code_exec_handler)
    app.add_route("/", languages_handler)

    print("Starting on {host}:{port}".format(host=application.host,
                                             port=application.port))
    WSGIServer((application.host, application.port), app).serve_forever()

    return 0
Example #32
0
def client():

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

    return testing.TestClient(api)
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()
Example #34
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
Example #35
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)
Example #36
0
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'):
Example #37
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))