Example #1
0
def get_request_token() -> DecodedBearerToken:
    """
    Returns token of the currently logged in user.
    Token is present in the header Authorization: Bearer <real_token>
    """
    return parse_request_token(
        auth_header=request.headers.get('Authorization'),
        jwt_secret=get_application_configuration().jwt_secret)
Example #2
0
def get_file_storage() -> FileStorage:
    """
    Creates instance of FileStorage.
    """
    conf = get_application_configuration()
    return MinIoStorage(conf.minio_url,
                        access_key=conf.minio_access_key,
                        secret_key=conf.minio_secret_key,
                        secure=conf.minio_secure)
Example #3
0
 def get(self):
     conf = get_application_configuration()
     logger.debug(
         f'Application version: {conf.code_version} in environment {conf.environment}.'
     )
     return jsonify({
         'version': conf.code_version,
         'environment': conf.environment
     })
Example #4
0
def refresh_token() -> str:
    """
    Refreshes JWT for users, does not work for SERVICE accounts and OTP tokens.
    """
    token = get_request_token()
    conf = get_application_configuration()
    return encode_auth_token(
        user_id=token.user_id,
        user_role=token.role,
        expiration=timedelta(days=conf.jwt_expiration_days),
        jwt_secret=conf.jwt_secret)
Example #5
0
def credentials_login(username: str, password: str) -> str:
    """
    Starts login flow for the given credentials. Returns valid JWT,
    if verification fails, some AuthenticationException is raised.
    """
    user = AppUserRepository.get_by_username(username)
    if not user or not password_matches_hash(user.pass_hash, password):
        logger.warning(f'User {username} credentials mismatch during login.')
        raise CredentialsMismatchException()

    conf = get_application_configuration()
    return encode_auth_token(
        user_id=user.id,
        user_role=user.role,
        expiration=timedelta(days=conf.jwt_expiration_days),
        jwt_secret=conf.jwt_secret)
def create_app() -> Flask:
    init_logging()
    app = Flask(__name__)
    # fix for https swagger - see https://github.com/python-restx/flask-restx/issues/58
    app.wsgi_app = ProxyFix(app.wsgi_app,
                            x_proto=1,
                            x_port=1,
                            x_for=1,
                            x_host=1,
                            x_prefix=1)

    def load_local_development_config():
        env_file = os.path.join(pathlib.Path().absolute(), '../.env')
        if os.path.exists(env_file):
            load_dotenv(dotenv_path=env_file, verbose=True)
            app.config['IS_LOCAL_DEV'] = True

    def connect_to_database(conf: ApplicationConfiguration):
        # connect to the database
        # docs https://flask.palletsprojects.com/en/1.1.x/patterns/sqlalchemy/
        init_db(
            DatabaseConfiguration(postgres_user=conf.postgres_user,
                                  postgres_password=conf.postgres_password,
                                  postgres_url=conf.postgres_url,
                                  postgres_db=conf.postgres_db))
        # process the migrations
        migrate_db(
            build_db_connection_string(
                postgres_user=conf.postgres_user,
                postgres_password=conf.postgres_password,
                postgres_url=conf.postgres_url,
                postgres_db=conf.postgres_db))

    def configure_apis():
        authorizations = {
            'bearer': {
                'type': 'apiKey',
                'in': 'header',
                'name': 'Authorization'
            }
        }

        api = Api(
            doc=_SWAGGER_URL,
            version='0.1',
            title='CEE Hacks 2020 API',
            authorizations=authorizations,
        )
        api.init_app(app)
        api.add_namespace(shared_models_namespace)
        api.add_namespace(job_namespace, path=f'{V1}/job')
        api.add_namespace(service_namespace, path=f'{V1}/service')
        api.add_namespace(user_namespace, path=f'{V1}/user')

    # def init_default_routes():
    #     # pylint: disable=unused-variable
    #     @app.route('/')
    #     def redirect_to_swagger():
    #         return redirect(_SWAGGER_URL, code=302)

    def configure_data_upload(conf: ApplicationConfiguration):
        app.config['MAX_CONTENT_LENGTH'] = conf.max_file_size_mb * 1024 * 1024

    def configure_context_hooks():
        # close session when the request is processed
        # https://flask.palletsprojects.com/en/1.1.x/patterns/sqlalchemy/
        @app.teardown_appcontext
        def shutdown_session(exception=None):
            session = get_db_session()
            if session:
                session.remove()

    def register_static_proxy():
        # serving main html which then asks for all javascript
        @app.route('/')
        def index_html():
            return send_from_directory('../../frontend/dist/frontend',
                                       'index.html')

        # used only if the there's no other endpoint registered
        # we need it to load static resources for the frontend
        @app.route('/<path:path>', methods=['GET'])
        def static_proxy(path):
            return send_from_directory('../../frontend/dist/frontend', path)

    def enable_cors():
        @app.after_request
        def add_headers(response):
            # allowed_origins = {
            #     '*'  # proxy on staging, support for swagger
            # }
            # origin = request.headers.get('origin')
            # if origin in allowed_origins:
            response.headers.add('Access-Control-Allow-Origin', '*')
            # response.headers.add('Access-Control-Allow-Origin', origin)
            response.headers.add('Access-Control-Allow-Headers',
                                 'Content-Type,Authorization')
            response.headers.add('Access-Control-Allow-Methods',
                                 'GET,POST,PUT')
            return response

    with app.app_context():
        # load configuration
        load_local_development_config()
        conf = get_application_configuration()
        # initialize database engine
        connect_to_database(conf)
        # configure context lifetime
        configure_context_hooks()
        # configure restrictions for file upload
        configure_data_upload(conf)
        # register basic routes
        # init_default_routes()
        register_static_proxy()
        # enable cors
        enable_cors()
        # finish configuration
        configure_apis()
        return app
Example #7
0
def create_app() -> Flask:
    app = Flask(__name__)
    # fix for https swagger - see https://github.com/python-restx/flask-restx/issues/58
    app.wsgi_app = ProxyFix(app.wsgi_app,
                            x_proto=1,
                            x_port=1,
                            x_for=1,
                            x_host=1,
                            x_prefix=1)

    def load_local_development_config():
        env_file = os.path.join(pathlib.Path().absolute(), '../.env')
        if os.path.exists(env_file):
            load_dotenv(dotenv_path=env_file, verbose=True)
            app.config['IS_LOCAL_DEV'] = True

    def connect_to_database(conf: ApplicationConfiguration):
        # connect to the database
        # docs https://flask.palletsprojects.com/en/1.1.x/patterns/sqlalchemy/
        init_db(
            DatabaseConfiguration(postgres_user=conf.postgres_user,
                                  postgres_password=conf.postgres_password,
                                  postgres_url=conf.postgres_url,
                                  postgres_db=conf.postgres_db))
        # process the migrations
        migrate_db(
            build_db_connection_string(
                postgres_user=conf.postgres_user,
                postgres_password=conf.postgres_password,
                postgres_url=conf.postgres_url,
                postgres_db=conf.postgres_db))

    def configure_apis():
        authorizations = {
            'bearer': {
                'type': 'apiKey',
                'in': 'header',
                'name': 'Authorization'
            }
        }

        api = Api(
            doc=_SWAGGER_URL,
            version='1.0',
            title='Breviary API',
            authorizations=authorizations,
        )
        api.init_app(app)
        api.add_namespace(shared_models_namespace)
        api.add_namespace(patients_namespace, path=f'{V1}/patients')
        api.add_namespace(service_namespace, path=f'{V1}/service')
        api.add_namespace(user_namespace, path=f'{V1}/user')
        api.add_namespace(recommendation_namespace,
                          path=f'{V1}/recommendation')

    def init_default_routes():
        # pylint: disable=unused-variable
        @app.route('/')
        def redirect_to_swagger():
            return redirect(_SWAGGER_URL, code=302)

    def configure_context_hooks():
        # close session when the request is processed
        # https://flask.palletsprojects.com/en/1.1.x/patterns/sqlalchemy/
        @app.teardown_appcontext
        def shutdown_session(exception=None):
            session = get_db_session()
            if session:
                session.remove()

    def enable_cors():
        @app.after_request
        def add_headers(response):
            # allowed_origins = {
            #     '*'  # proxy on staging, support for swagger
            # }
            # origin = request.headers.get('origin')
            # if origin in allowed_origins:
            response.headers.add('Access-Control-Allow-Origin', '*')
            # response.headers.add('Access-Control-Allow-Origin', origin)
            response.headers.add('Access-Control-Allow-Headers',
                                 'Content-Type,Authorization')
            response.headers.add('Access-Control-Allow-Methods',
                                 'GET,POST,PUT')
            return response

    with app.app_context():
        get_logger()
        # load configuration
        load_local_development_config()
        conf = get_application_configuration()
        # initialize database engine
        connect_to_database(conf)
        # configure context lifetime
        configure_context_hooks()
        # register basic routes
        init_default_routes()
        # enable cors
        enable_cors()
        # finish configuration
        configure_apis()
        return app