Exemple #1
0
def create_app(db_connection_string=None, testing=None):
    app = Flask(__name__)

    try:
        secret_key = faraday.server.config.faraday_server.secret_key
    except Exception:
        # Now when the config file does not exist it doesn't enter in this
        # condition, but it could happen in the future. TODO check
        save_new_secret_key(app)
    else:
        if secret_key is None:
            # This is what happens now when the config file doesn't exist.
            # TODO check
            save_new_secret_key(app)
        else:
            app.config['SECRET_KEY'] = secret_key

    if faraday.server.config.faraday_server.agent_token is None:
        save_new_agent_creation_token()

    login_failed_message = ("Invalid username or password", 'error')

    app.config.update({
        'SECURITY_PASSWORD_SINGLE_HASH': True,
        'WTF_CSRF_ENABLED': False,
        'SECURITY_USER_IDENTITY_ATTRIBUTES': ['username'],
        'SECURITY_POST_LOGIN_VIEW': '/_api/session',
        'SECURITY_POST_LOGOUT_VIEW': '/_api/login',
        'SECURITY_POST_CHANGE_VIEW': '/_api/change',
        'SECURITY_CHANGEABLE': True,
        'SECURITY_SEND_PASSWORD_CHANGE_EMAIL': False,
        'SECURITY_MSG_USER_DOES_NOT_EXIST': login_failed_message,
        'SECURITY_TOKEN_AUTHENTICATION_HEADER': 'Authorization',

        # The line bellow should not be necessary because of the
        # CustomLoginForm, but i'll include it anyway.
        'SECURITY_MSG_INVALID_PASSWORD': login_failed_message,

        'SESSION_TYPE': 'filesystem',
        'SESSION_FILE_DIR': faraday.server.config.FARADAY_SERVER_SESSIONS_DIR,

        'SQLALCHEMY_TRACK_MODIFICATIONS': False,
        'SQLALCHEMY_RECORD_QUERIES': True,
        # app.config['SQLALCHEMY_ECHO'] = True
        'SECURITY_PASSWORD_SCHEMES': [
            'bcrypt',  # This should be the default value
            # 'des_crypt',
            'pbkdf2_sha1',  # Used by CouchDB passwords
            # 'pbkdf2_sha256',
            # 'pbkdf2_sha512',
            # 'sha256_crypt',
            # 'sha512_crypt',
            'plaintext',  # TODO: remove it
        ],
        'PERMANENT_SESSION_LIFETIME': datetime.timedelta(hours=12),
        'SESSION_COOKIE_NAME': 'faraday_session_2',
        'SESSION_COOKIE_SAMESITE': 'Lax',
    })

    store = FilesystemStore(app.config['SESSION_FILE_DIR'])
    prefixed_store = PrefixDecorator('sessions_', store)
    KVSessionExtension(prefixed_store, app)
    user_logged_out.connect(expire_session, app)

    storage_path = faraday.server.config.storage.path
    if not storage_path:
        logger.warn('No storage section or path in the .faraday/config/server.ini. Setting the default value to .faraday/storage')
        storage_path = setup_storage_path()

    if not DepotManager.get('default'):
        if testing:
            DepotManager.configure('default', {
                'depot.storage_path': '/tmp'
            })
        else:
            DepotManager.configure('default', {
                'depot.storage_path': storage_path
            })

    check_testing_configuration(testing, app)

    try:
        app.config['SQLALCHEMY_DATABASE_URI'] = db_connection_string or faraday.server.config.database.connection_string.strip("'")
    except AttributeError:
        logger.info('Missing [database] section on server.ini. Please configure the database before running the server.')
    except NoOptionError:
        logger.info('Missing connection_string on [database] section on server.ini. Please configure the database before running the server.')

    from faraday.server.models import db # pylint:disable=import-outside-toplevel
    db.init_app(app)
    #Session(app)

    # Setup Flask-Security
    app.user_datastore = SQLAlchemyUserDatastore(
        db,
        user_model=User,
        role_model=None)  # We won't use flask security roles feature
    Security(app, app.user_datastore, login_form=CustomLoginForm)
    # Make API endpoints require a login user by default. Based on
    # https://stackoverflow.com/questions/13428708/best-way-to-make-flask-logins-login-required-the-default

    app.view_functions['security.login'].is_public = True
    app.view_functions['security.logout'].is_public = True

    app.debug = faraday.server.config.is_debug_mode()
    minify_json_output(app)

    for handler in LOGGING_HANDLERS:
        app.logger.addHandler(handler)

    register_blueprints(app)
    register_handlers(app)

    app.view_functions['agent_api.AgentCreationView:post'].is_public = True

    return app
Exemple #2
0
def create_app(db_connection_string=None, testing=None):
    class CustomFlask(Flask):
        SKIP_RULES = [  # These endpoints will be removed for v3
            '/v3/ws/<workspace_name>/hosts/bulk_delete/',
            '/v3/ws/<workspace_name>/vulns/bulk_delete/',
            '/v3/ws/<workspace_id>/change_readonly/',
            '/v3/ws/<workspace_id>/deactivate/',
            '/v3/ws/<workspace_id>/activate/',
        ]

        def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
            # Flask registers views when an application starts
            # do not add view from SKIP_VIEWS
            for rule_ in CustomFlask.SKIP_RULES:
                if rule_ == rule:
                    return
            return super().add_url_rule(rule, endpoint, view_func, **options)

    app = CustomFlask(__name__, static_folder=None)

    try:
        secret_key = faraday.server.config.faraday_server.secret_key
    except Exception:
        # Now when the config file does not exist it doesn't enter in this
        # condition, but it could happen in the future. TODO check
        save_new_secret_key(app)
    else:
        if secret_key is None:
            # This is what happens now when the config file doesn't exist.
            # TODO check
            save_new_secret_key(app)
        else:
            app.config['SECRET_KEY'] = secret_key

    if faraday.server.config.faraday_server.agent_registration_secret is None:
        save_new_agent_creation_token_secret()

    login_failed_message = ("Invalid username or password", 'error')

    app.config.update({
        'SECURITY_BACKWARDS_COMPAT_AUTH_TOKEN':
        True,
        'SECURITY_PASSWORD_SINGLE_HASH':
        True,
        'WTF_CSRF_ENABLED':
        False,
        'SECURITY_USER_IDENTITY_ATTRIBUTES': [{
            'username': {
                'mapper': uia_username_mapper
            }
        }],
        'SECURITY_POST_LOGIN_VIEW':
        '/_api/session',
        'SECURITY_POST_CHANGE_VIEW':
        '/_api/change',
        'SECURITY_RESET_PASSWORD_TEMPLATE':
        '/security/reset.html',
        'SECURITY_POST_RESET_VIEW':
        '/',
        'SECURITY_SEND_PASSWORD_RESET_EMAIL':
        True,
        # For testing porpouse
        'SECURITY_EMAIL_SENDER':
        "*****@*****.**",
        'SECURITY_CHANGEABLE':
        True,
        'SECURITY_SEND_PASSWORD_CHANGE_EMAIL':
        False,
        'SECURITY_MSG_USER_DOES_NOT_EXIST':
        login_failed_message,
        'SECURITY_TOKEN_AUTHENTICATION_HEADER':
        'Authorization',

        # The line bellow should not be necessary because of the
        # CustomLoginForm, but i'll include it anyway.
        'SECURITY_MSG_INVALID_PASSWORD':
        login_failed_message,
        'SESSION_TYPE':
        'filesystem',
        'SESSION_FILE_DIR':
        faraday.server.config.FARADAY_SERVER_SESSIONS_DIR,
        'SQLALCHEMY_TRACK_MODIFICATIONS':
        False,
        'SQLALCHEMY_RECORD_QUERIES':
        True,
        # app.config['SQLALCHEMY_ECHO'] = True
        'SECURITY_PASSWORD_SCHEMES': [
            'bcrypt',  # This should be the default value
            # 'des_crypt',
            # 'pbkdf2_sha256',
            # 'pbkdf2_sha512',
            # 'sha256_crypt',
            # 'sha512_crypt',
        ],
        'PERMANENT_SESSION_LIFETIME':
        datetime.timedelta(hours=int(
            faraday.server.config.faraday_server.session_timeout or 12)),
        'SESSION_COOKIE_NAME':
        'faraday_session_2',
        'SESSION_COOKIE_SAMESITE':
        'Lax',
    })

    store = FilesystemStore(app.config['SESSION_FILE_DIR'])
    prefixed_store = PrefixDecorator('sessions_', store)
    KVSessionExtension(prefixed_store, app)
    user_logged_in.connect(user_logged_in_succesfull, app)
    user_logged_out.connect(expire_session, app)

    storage_path = faraday.server.config.storage.path
    if not storage_path:
        logger.warn(
            'No storage section or path in the .faraday/config/server.ini. Setting the default value to .faraday/storage'
        )
        storage_path = setup_storage_path()

    if not DepotManager.get('default'):
        if testing:
            DepotManager.configure(
                'default',
                {
                    'depot.storage_path': '/tmp'  # nosec
                })
        else:
            DepotManager.configure('default',
                                   {'depot.storage_path': storage_path})

    check_testing_configuration(testing, app)

    try:
        app.config[
            'SQLALCHEMY_DATABASE_URI'] = db_connection_string or faraday.server.config.database.connection_string.strip(
                "'")
    except AttributeError:
        logger.info(
            'Missing [database] section on server.ini. Please configure the database before running the server.'
        )
    except NoOptionError:
        logger.info(
            'Missing connection_string on [database] section on server.ini. Please configure the database before running the server.'
        )

    from faraday.server.models import db  # pylint:disable=import-outside-toplevel
    db.init_app(app)
    # Session(app)

    # Setup Flask-Security
    app.user_datastore = SQLAlchemyUserDatastore(
        db, user_model=User,
        role_model=None)  # We won't use flask security roles feature

    from faraday.server.api.modules.agent import agent_creation_api  # pylint: disable=import-outside-toplevel

    app.limiter = Limiter(app, key_func=get_remote_address, default_limits=[])
    if not testing:
        app.limiter.limit(faraday.server.config.limiter_config.login_limit)(
            agent_creation_api)

    app.register_blueprint(agent_creation_api)

    Security(app, app.user_datastore, login_form=CustomLoginForm)
    # Make API endpoints require a login user by default. Based on
    # https://stackoverflow.com/questions/13428708/best-way-to-make-flask-logins-login-required-the-default

    app.view_functions['security.login'].is_public = True
    app.view_functions['security.logout'].is_public = True
    app.debug = faraday.server.config.is_debug_mode()
    minify_json_output(app)

    for handler in LOGGING_HANDLERS:
        app.logger.addHandler(handler)
    app.logger.propagate = False
    register_blueprints(app)
    register_handlers(app)

    app.view_functions[
        'agent_creation_api.AgentCreationView:post'].is_public = True
    app.view_functions[
        'agent_creation_api.AgentCreationV3View:post'].is_public = True

    return app