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
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