######### # Copyright (c) 2013 GigaSpaces Technologies Ltd. All rights reserved # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # * See the License for the specific language governing permissions and # * limitations under the License. from collections import namedtuple from flask import Flask from flask_migrate import Migrate from flask_security import Security from manager_rest import config, utils from manager_rest.storage import user_datastore, db from manager_rest.storage.models import User, Tenant from manager_rest.config import instance as manager_config def setup_flask_app(manager_ip='localhost', driver='', hash_salt=None, secret_key=None): """Setup a functioning flask app, when working outside the rest-service :param manager_ip: The IP of the manager :param driver: SQLA driver for postgres (e.g. pg8000) :param hash_salt: The salt to be used when creating user passwords :param secret_key: Secret key used when hashing flask tokens :return: A Flask app """ app = Flask(__name__) db_uri = _get_postgres_db_uri(manager_ip, driver) app.config['SQLALCHEMY_DATABASE_URI'] = db_uri app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['ENV'] = 'production' set_flask_security_config(app, hash_salt, secret_key) Security(app=app, datastore=user_datastore) Migrate(app=app, db=db) db.init_app(app) app.app_context().push() return app def _get_postgres_db_uri(manager_ip, driver): """Get a valid SQLA DB URI""" dialect = 'postgresql+{0}'.format(driver) if driver else 'postgres' conf = get_postgres_conf(manager_ip) conn_string = '{dialect}://{username}:{password}@{host}/{db_name}'.format( dialect=dialect, username=conf.username, password=conf.password, host=conf.host, db_name=conf.db_name ) if config.instance.postgresql_ssl_enabled: params = {} ssl_mode = 'verify-ca' if config.instance.postgresql_ssl_client_verification: ssl_mode = 'verify-full' params.update({ 'sslcert': config.instance.postgresql_ssl_cert_path, 'sslkey': config.instance.postgresql_ssl_key_path, }) params.update({ 'sslmode': ssl_mode, 'sslrootcert': config.instance.postgresql_ca_cert_path }) if any(params.values()): query = '&'.join('{0}={1}'.format(key, value) for key, value in params.items() if value) conn_string = '{0}?{1}'.format(conn_string, query) return conn_string def get_postgres_conf(manager_ip='localhost'): """Return a namedtuple with info used to connect to cloudify's PG DB """ # can't load from db yet - we're just loading the settings to connect to # the db at all manager_config.load_configuration(from_db=False) conf = namedtuple('PGConf', 'host username password db_name') return conf( host=manager_config.postgresql_host or manager_ip, username=manager_config.postgresql_username or 'cloudify', password=manager_config.postgresql_password or 'cloudify', db_name=manager_config.postgresql_db_name or 'cloudify_db', ) def set_flask_security_config(app, hash_salt=None, secret_key=None): """Set all necessary Flask-Security configurations :param app: Flask app object :param hash_salt: The salt to be used when creating user passwords :param secret_key: Secret key used when hashing flask tokens """ hash_salt = hash_salt or config.instance.security_hash_salt secret_key = secret_key or config.instance.security_secret_key # Make sure that it's possible to get users from the datastore # by username and not just by email (the default behavior) app.config['SECURITY_USER_IDENTITY_ATTRIBUTES'] = 'username, email' app.config['SECURITY_PASSWORD_HASH'] = 'pbkdf2_sha256' app.config['SECURITY_HASHING_SCHEMES'] = ['pbkdf2_sha256'] app.config['SECURITY_DEPRECATED_HASHING_SCHEMES'] = [] app.config['SECURITY_TOKEN_MAX_AGE'] = 36000 # 10 hours app.config['SECURITY_PASSWORD_SALT'] = hash_salt app.config['SECURITY_REMEMBER_SALT'] = hash_salt app.config['SECRET_KEY'] = secret_key def set_tenant_in_app(tenant): """Set the tenant as the current tenant in the flask app. Requires app context (using `with app.app_context()` or push as returned by setup_flask_app""" utils.set_current_tenant(tenant) def get_tenant_by_name(tenant_name): """Get the tenant name, or fail noisily. """ tenant = Tenant.query.filter_by(name=tenant_name).first() if not tenant: raise Exception( 'Could not restore into tenant "{name}" as this tenant does ' 'not exist.'.format(name=tenant_name) ) return tenant def set_admin_current_user(app): """Set the admin as the current user in the flask app :return: The admin user """ admin = User.query.get(0) # This line is necessary for the `reload_user` method - we add a mock # request context to the flask stack app.test_request_context().push() # And then load the admin as the currently active user app.extensions['security'].login_manager.reload_user(admin) return admin