def test_nullable_username(app, sqlalchemy_datastore): # sqlalchemy datastore uses fsqlav2 which has username as unique and nullable # make sure can register multiple users with no username # Note that current WTForms (2.2.1) has a bug where StringFields can never be # None - it changes them to an empty string. DBs don't like that if you have # your column be 'nullable'. class NullableStringField(StringField): def process_formdata(self, valuelist): if valuelist: self.data = valuelist[0] class MyRegisterForm(ConfirmRegisterForm): username = NullableStringField("Username") app.config["SECURITY_CONFIRM_REGISTER_FORM"] = MyRegisterForm security = Security(datastore=sqlalchemy_datastore) security.init_app(app) client = app.test_client() data = dict(email="*****@*****.**", password="******", password_confirm="password") response = client.post( "/register", json=data, headers={"Content-Type": "application/json"} ) assert response.status_code == 200 logout(client) data = dict(email="*****@*****.**", password="******", password_confirm="password") response = client.post( "/register", json=data, headers={"Content-Type": "application/json"} ) assert response.status_code == 200
def init_app(app, user_datastore, LoginForm): def load_user(user_id): return user_datastore.get_user(user_id) def load_user_from_request(request): apikey = request.headers.environ.get("HTTP_X_API_KEY", None) if apikey: user = user_datastore.find_user(apikey=apikey) if not user: return None else: auth = request.headers.get("Authorization") if not auth or auth.count(":") != 1: return None login, password = auth.split(":") user = user_datastore.find_user(email=login.strip()) if user is None: return None if not verify_and_update_password(password.strip(), user): return None return user if login_user(user) else None security = Security() security.init_app(app, user_datastore, login_form=LoginForm) app.login_manager.request_loader(load_user_from_request) app.login_manager.user_loader(load_user) user_logged_out.connect(invalidate_user)
def init_extensions(app): """ Initialize extensions registered for this app :param app: :return: """ db_adapter = SQLAlchemyUserDatastore(db, User, Role) user_manager = Security() assets_env.init_app(app) mail.init_app(app) celery.init_app(app) compress.init_app(app) csrf.init_app(app) review_images_factory(app.testing, app) db.init_app(app) debug_toolbar.init_app(app) migrate.init_app(app, db, directory='store/migrations') resize.init_app(app) socketio.init_app(app) if app.config.get("MODE") == assets.AppModes.PRODUCTION: sentry.init_app(app, logging=True) from auth.forms import ExtendedRegisterForm, ExtendedLoginForm user_manager.init_app(app, db_adapter, register_form=ExtendedRegisterForm, login_form=ExtendedLoginForm, confirm_register_form=ExtendedRegisterForm)
def test_deprecated(app, sqlalchemy_datastore): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") security = Security() security.init_app(app, sqlalchemy_datastore) assert len(w) == 1 assert issubclass(w[-1].category, DeprecationWarning) assert "deprecated" in str(w[-1].message)
def test_init_app_kwargs_override_constructor_kwargs(app, datastore): security = Security(datastore=datastore, login_form='__init__login_form', register_form='__init__register_form') security.init_app(app, login_form='init_app_login_form') assert security.login_form == 'init_app_login_form' assert security.register_form == '__init__register_form'
def create_app(self): app = super(UserRegistrationDbTests, self).create_app() app.config['NOI_CAN_UNCONFIRMED_USERS_FULLY_REGISTER'] = False security = Security() security.init_app(app, datastore=DeploySQLAlchemyUserDatastore( db, User, Role )) return app
def create_app(): app = flask.Flask(__name__) app.config.from_object(config['development']) config['development'].init_app(app) app.config["SECRET_KEY"] = "SunshineSucks" app.config['SECURITY_REGISTERABLE'] = True app.config["SECURITY_CONFIRMABLE"] = False app.config["SECURITY_SEND_REGISTER_EMAIL"] = False app.config["SECURITY_PASSWORD_HASH"] = 'pbkdf2_sha512' app.config['SECURITY_PASSWORD_SALT'] = 'xxxxxxxxxxxx' app.config['SECRET_KEY'] = 'FmG9yqMxVfb9aoEVpn6J' db.init_app(app) login_manager.init_app(app) api = Api(app) from app.routing.StoryHandler import StoryListHandler, StoryHandler, StoryBookmarkHandler, TestStoryHandler from app.routing.AdditionHandler import AdditionListHandler, ActiveAdditionHandler, AdditionBookmarkHandler from app.routing.UserHandler import LoginHandler, UserListHandler, UserHandler, TestUserHandler from app.routing.CharacterHandler import TestCharacterHandler, CharacterListHandler from app.routing.TraitHandler import TestTraitHandler from app.models import User as db_user, Role user_datastore = SQLAlchemyUserDatastore(db, db_user, Role) security = Security() security.init_app(app, user_datastore) #USERS api.add_resource(LoginHandler, '/user/login') api.add_resource(UserListHandler, '/users') api.add_resource(UserHandler, '/user/<user_id>') #BASE STORIES api.add_resource(StoryBookmarkHandler, '/story/<story_id>/likes') api.add_resource(StoryListHandler, '/stories') api.add_resource(StoryHandler, '/story/<story_id>') #BASE EXTENSIONS api.add_resource(AdditionListHandler, '/<story_id>/additions') api.add_resource(AdditionBookmarkHandler, '/story/<story_id>/additions/<addition_id>/bookmarks') #STORY CREATOR CHARACTERS api.add_resource(CharacterListHandler, '/characters') #Test Routes api.add_resource(TestCharacterHandler, '/test/characters') api.add_resource(TestTraitHandler, '/test/traits') api.add_resource(TestStoryHandler, '/test/stories') api.add_resource(TestUserHandler, '/test/users') return app
def create_app(**config_overrides): """Factory to create the Flask application""" app = Flask(__name__) # Load config. app.config.from_object(config) # apply overrides app.config.update(config_overrides) app.wsgi_app = ReverseProxied(app.wsgi_app) app.jinja_env.auto_reload = True database.db.initialize_db(app) # Setup Flask-Mail mail = Mail() mail.init_app(app) # Setup Flask-Security security = Security() security.init_app(app, database.models.user_datastore) # Create an admin user # TODO: Move this to mongo-init.js @app.before_first_request def create_admin_user(): database.models.user_datastore.create_user( email=app.config['ADMIN_EMAIL'], password=hash_password(app.config['ADMIN_PASSWORD']), ) admin_role = database.models.user_datastore.find_or_create_role( name='admin') database.models.user_datastore.add_role_to_user( user=app.config['ADMIN_EMAIL'], role=admin_role) app.register_blueprint(general.general_bp, url_prefix='/') app.register_blueprint(experiments.api.experiments_api_bp, url_prefix='/api/experiments') app.register_blueprint(experiments.views.experiments_bp, url_prefix='/experiments') app.register_blueprint(samples.api.samples_api_bp, url_prefix='/api/samples') app.register_blueprint(samples.views.samples_bp, url_prefix='/samples') app.register_blueprint(training.api.training_api_bp, url_prefix='/api/training') app.register_blueprint(training.views.training_bp, url_prefix='/training') # toolbar = DebugToolbarExtension(app) return app
def test_email_not_identity(app, sqlalchemy_datastore, get_message): # Test that can register/confirm with email even if it isn't an IDENTITY_ATTRIBUTE from flask_security import ConfirmRegisterForm, Security, unique_identity_attribute from wtforms import StringField, validators class MyRegisterForm(ConfirmRegisterForm): username = StringField( "Username", validators=[validators.data_required(), unique_identity_attribute], ) app.config["SECURITY_CONFIRM_REGISTER_FORM"] = MyRegisterForm security = Security(datastore=sqlalchemy_datastore) security.init_app(app) client = app.test_client() with capture_registrations() as registrations: data = dict(email="*****@*****.**", username="******", password="******") response = client.post("/register", data=data, follow_redirects=True) assert b"*****@*****.**" in response.data token = registrations[0]["confirm_token"] response = client.get("/confirm/" + token, headers={"Accept": "application/json"}) assert response.status_code == 302 assert response.location == "http://localhost/" logout(client) # check that username must be unique data = dict(email="*****@*****.**", username="******", password="******") response = client.post("/register", data=data, headers={"Accept": "application/json"}) assert response.status_code == 400 assert "is already associated" in response.json["response"]["errors"][ "username"][0] # log in with username - this uses the age-old hack that although the form's # input label says "email" - it in fact will accept any identity attribute. response = client.post( "/login", data=dict(email="mary", password="******"), follow_redirects=True, ) assert b"<p>Welcome mary</p>" in response.data
def test_form_required(app, sqlalchemy_datastore): class MyLoginForm(LoginForm): myfield = StringField("My Custom Field", validators=[Required()]) app.config["SECURITY_LOGIN_FORM"] = MyLoginForm security = Security(datastore=sqlalchemy_datastore) security.init_app(app) client = app.test_client() response = client.post("/login", content_type="application/json") assert response.status_code == 400 assert b"myfield" in response.data
def _conf_security_mongo(app): '''Flask-Security MongoDB''' from flask_security import Security, MongoEngineUserDatastore from flask_social_blueprint.core import SocialBlueprint from shortener_url.backends.mongo import models extensions.login.init_app(app) extensions.login.login_view = "/login" security = Security() datastore = MongoEngineUserDatastore(app.db, models.User, models.Role) security = security.init_app(app, datastore) security.send_mail_task(_send_mail) SocialBlueprint.init_bp(app, models.SocialConnection, url_prefix="/_social") #@app.before_first_request #def before_first_request(): # for m in [models.User, models.Role, models.SocialConnection]: # m.drop_collection() @extensions.login.user_loader def load_user(user_id): return models.User.objects(_id=user_id)
def test_form_required_local_message(app, sqlalchemy_datastore): """ Test having a local message (not xlatable and not part of MSG_ config.""" class MyLoginForm(LoginForm): myfield = StringField("My Custom Field", validators=[Required(message="hi")]) app.config["SECURITY_LOGIN_FORM"] = MyLoginForm security = Security(datastore=sqlalchemy_datastore) security.init_app(app) client = app.test_client() response = client.post("/login", content_type="application/json") assert response.status_code == 400 assert b"myfield" in response.data
def test_no_email_sender(app): """ Verify that if SECURITY_EMAIL_SENDER is default (which is a local proxy) that send_mail picks up MAIL_DEFAULT_SENDER. """ app.config["MAIL_DEFAULT_SENDER"] = "*****@*****.**" class TestUser(object): def __init__(self, email): self.email = email security = Security() security.init_app(app) with app.app_context(): user = TestUser("*****@*****.**") with app.mail.record_messages() as outbox: send_mail("Test Default Sender", user.email, "welcome", user=user) assert 1 == len(outbox) assert "*****@*****.**" == outbox[0].sender
def test_init_app_kwargs_override_constructor_kwargs(app, datastore): class ConLoginForm(LoginForm): pass class ConRegisterForm(RegisterForm): pass class InitLoginForm(LoginForm): pass security = Security( datastore=datastore, login_form=ConLoginForm, register_form=ConRegisterForm, ) security.init_app(app, login_form=InitLoginForm) assert security.login_form == InitLoginForm assert security.register_form == ConRegisterForm
def test_easy_password(app, sqlalchemy_datastore): class MyRegisterForm(ConfirmRegisterForm): username = StringField("Username") app.config["SECURITY_CONFIRM_REGISTER_FORM"] = MyRegisterForm security = Security(datastore=sqlalchemy_datastore) security.init_app(app) client = app.test_client() # With zxcvbn data = dict( email="*****@*****.**", username="******", password="******", password_confirm="mattmatt2", ) response = client.post( "/register", json=data, headers={"Content-Type": "application/json"} ) assert response.headers["Content-Type"] == "application/json" assert response.status_code == 400 # Response from zxcvbn assert "Repeats like" in response.json["response"]["errors"]["password"][0] # Test that username affects password selection data = dict( email="*****@*****.**", username="******", password="******", password_confirm="JoeTheDude", ) response = client.post( "/register", json=data, headers={"Content-Type": "application/json"} ) assert response.headers["Content-Type"] == "application/json" assert response.status_code == 400 # Response from zxcvbn assert ( "Password not complex enough" in response.json["response"]["errors"]["password"][0] )
def test_sender_tuple(app, sqlalchemy_datastore): """Verify that if sender is a (name, address) tuple, in the received email sender is properly formatted as "name <address>" """ app.config["MAIL_DEFAULT_SENDER"] = ("Test User", "*****@*****.**") class TestUser: def __init__(self, email): self.email = email security = Security() security.init_app(app, sqlalchemy_datastore) with app.app_context(): app.try_trigger_before_first_request_functions() user = TestUser("*****@*****.**") with app.mail.record_messages() as outbox: send_mail("Test Tuple Sender", user.email, "welcome", user=user) assert 1 == len(outbox) assert "Test User <*****@*****.**>" == outbox[0].sender
def init_app(self, app, datastore=None, register_blueprint=False, login_form=None, confirm_register_form=None, register_form=None, forgot_password_form=None, reset_password_form=None, change_password_form=None, send_confirmation_form=None, passwordless_login_form=None, anonymous_user=None): Security.init_app(self, app, datastore, register_blueprint, login_form, confirm_register_form, register_form, forgot_password_form, reset_password_form, change_password_form, send_confirmation_form, passwordless_login_form, anonymous_user) app.login_manager.blueprint_login_views = app.config[ 'BLUEPRINT_LOGIN_VIEWS']
def test_no_email_sender(app, sqlalchemy_datastore): """Verify that if SECURITY_EMAIL_SENDER is default (which is a local proxy) that send_mail picks up MAIL_DEFAULT_SENDER. """ app.config["MAIL_DEFAULT_SENDER"] = "*****@*****.**" class TestUser: def __init__(self, email): self.email = email security = Security() security.init_app(app, sqlalchemy_datastore) with app.app_context(): app.try_trigger_before_first_request_functions() user = TestUser("*****@*****.**") with app.mail.record_messages() as outbox: send_mail("Test Default Sender", user.email, "welcome", user=user) assert 1 == len(outbox) assert "*****@*****.**" == outbox[0].sender
def test_custom_forms_via_config(app, sqlalchemy_datastore): class MyLoginForm(LoginForm): email = StringField("My Login Email Address Field") class MyRegisterForm(RegisterForm): email = StringField("My Register Email Address Field") app.config["SECURITY_LOGIN_FORM"] = MyLoginForm app.config["SECURITY_REGISTER_FORM"] = MyRegisterForm security = Security(datastore=sqlalchemy_datastore) security.init_app(app) client = app.test_client() response = client.get("/login") assert b"My Login Email Address Field" in response.data response = client.get("/register") assert b"My Register Email Address Field" in response.data
def test_custom_forms_via_config(app, sqlalchemy_datastore): class MyLoginForm(LoginForm): email = StringField('My Login Email Address Field') class MyRegisterForm(RegisterForm): email = StringField('My Register Email Address Field') app.config['SECURITY_LOGIN_FORM'] = MyLoginForm app.config['SECURITY_REGISTER_FORM'] = MyRegisterForm security = Security(datastore=sqlalchemy_datastore) security.init_app(app) client = app.test_client() response = client.get('/login') assert b'My Login Email Address Field' in response.data response = client.get('/register') assert b'My Register Email Address Field' in response.data
def test_reuse_security_object(sqlalchemy_datastore): # See: https://github.com/Flask-Middleware/flask-security/issues/518 # Let folks re-use the Security object (mostly for testing). security = Security(datastore=sqlalchemy_datastore) app = Flask(__name__) app.response_class = Response app.debug = True app.config["SECRET_KEY"] = "secret" app.config["TESTING"] = True security.init_app(app) assert hasattr(app, "login_manager") app = Flask(__name__) app.response_class = Response app.debug = True app.config["SECRET_KEY"] = "secret" app.config["TESTING"] = True security.init_app(app) assert hasattr(app, "login_manager")
def test_form_required_local_message(app, sqlalchemy_datastore): """Test having a local message (not xlatable and not part of MSG_ config.""" msg = "hi! did you forget me?" class MyLoginForm(LoginForm): myfield = StringField("My Custom Field", validators=[Required(message=msg)]) app.config["SECURITY_LOGIN_FORM"] = MyLoginForm security = Security(datastore=sqlalchemy_datastore) security.init_app(app) client = app.test_client() response = client.post("/login", content_type="application/json") assert response.status_code == 400 assert b"myfield" in response.data assert msg.encode("utf-8") in response.data # WTforms 2.x incorrectly catches ValueError and sets that as the form error. # Our config_value routine raises ValueError for missing config items.. assert b"Key" not in response.data
def init_app(app): from flask_login import LoginManager login_manager = LoginManager() login_manager.init_app(app) login_manager.user_loader(load_user) login_manager.login_view = "/login" # Setup Flask-Security security = Security() security = security.init_app(app, AppEngineUserDatastore(User, Role)) security.send_mail_task(send_mail) from flask_social_blueprint.core import SocialBlueprint SocialBlueprint.init_bp(app, SocialConnection, url_prefix="/_social")
def init_app(app): # Flask-Login # https://flask-login.readthedocs.org/en/latest/ from flask_login import LoginManager login_manager = LoginManager() login_manager.init_app(app) login_manager.user_loader(load_user) login_manager.login_view = "/login" # Setup Flask-Security security = Security() security = security.init_app(app, SQLAlchemyUserDatastore(db, User, Role)) security.send_mail_task(send_mail) from flask_social_blueprint.core import SocialBlueprint SocialBlueprint.init_bp(app, SocialConnection, url_prefix="/_social")
def init_app(app): from flask_login import LoginManager login_manager = LoginManager() login_manager.init_app(app) login_manager.user_loader(load_user) login_manager.login_view = "/login" # Setup Flask-Security from flask_security.forms import ChangePasswordForm security = Security() security = security.init_app(app, SQLAlchemyUserDatastore(db, User, Role), change_password_form=ChangePasswordForm) security.send_mail_task(send_mail) from flask_social_blueprint.core import SocialBlueprint SocialBlueprint.init_bp(app, SocialConnection, url_prefix="/auth")
def init_app(app): login_manager = LoginManager() login_manager.init_app(app) login_manager.user_loader(load_user) login_manager.login_view = '/login' # Setup Flask-Security security = Security() security = security.init_app(app, MongoEngineUserDatastore(db, User, Role)) security.send_mail_task(send_mail) from flask_social_blueprint.core import SocialBlueprint SocialBlueprint.init_bp(app, SocialConnection, url_prefix='/_social') state = app.extensions['security'] state.render_template = render_template app.extensions['security'] = state
def init_app(app): # Flask-Login # https://flask-login.readthedocs.org/en/latest/ from flask_login import LoginManager login_manager = LoginManager() login_manager.init_app(app) login_manager.user_loader(load_user) login_manager.login_view = "/login" # Setup Flask-Security security = Security() security = security.init_app(app, MongoEngineUserDatastore(db, User, Role)) security.send_mail_task(send_mail) from flask_social_blueprint.core import SocialBlueprint SocialBlueprint.init_bp(app, SocialConnection, url_prefix="/_social") @app.before_first_request def before_first_request(): for m in [User, Role, SocialConnection]: m.drop_collection()
def create_app(app_name=None): # Configuration settings import config if not app_name: app_name = config.APP_NAME # Check if app is created for CLI operations or Web cli_mode = False if app_name.endswith('-cli'): cli_mode = True # Only enable password related functionality in server mode. if config.SERVER_MODE is True: # Some times we need to access these config params where application # context is not available (we can't use current_app.config in those # cases even with current_app.app_context()) # So update these params in config itself. # And also these updated config values will picked up by application # since we are updating config before the application instance is # created. config.SECURITY_RECOVERABLE = True config.SECURITY_CHANGEABLE = True # Now we'll open change password page in alertify dialog # we don't want it to redirect to main page after password # change operation so we will open the same password change page again. config.SECURITY_POST_CHANGE_VIEW = 'browser.change_password' """Create the Flask application, startup logging and dynamically load additional modules (blueprints) that are found in this directory.""" app = PgAdmin(__name__, static_url_path='/static') # Removes unwanted whitespace from render_template function app.jinja_env.trim_blocks = True app.config.from_object(config) app.config.update(dict(PROPAGATE_EXCEPTIONS=True)) ########################################################################## # Setup logging and log the application startup ########################################################################## # We won't care about errors in the logging system, we are more # interested in application errors. logging.raiseExceptions = False # Add SQL level logging, and set the base logging level logging.addLevelName(25, 'SQL') app.logger.setLevel(logging.DEBUG) app.logger.handlers = [] # We also need to update the handler on the webserver in order to see # request. Setting the level prevents werkzeug from setting up it's own # stream handler thus ensuring all the logging goes through the pgAdmin # logger. logger = logging.getLogger('werkzeug') logger.setLevel(config.CONSOLE_LOG_LEVEL) # Set SQLITE_PATH to TEST_SQLITE_PATH while running test cases if ('PGADMIN_TESTING_MODE' in os.environ and os.environ['PGADMIN_TESTING_MODE'] == '1'): config.SQLITE_PATH = config.TEST_SQLITE_PATH config.MASTER_PASSWORD_REQUIRED = False config.UPGRADE_CHECK_ENABLED = False if not cli_mode: # Ensure the various working directories exist from pgadmin.setup import create_app_data_directory create_app_data_directory(config) # File logging fh = logging.FileHandler(config.LOG_FILE, encoding='utf-8') fh.setLevel(config.FILE_LOG_LEVEL) fh.setFormatter(logging.Formatter(config.FILE_LOG_FORMAT)) app.logger.addHandler(fh) logger.addHandler(fh) # Console logging ch = logging.StreamHandler() ch.setLevel(config.CONSOLE_LOG_LEVEL) ch.setFormatter(logging.Formatter(config.CONSOLE_LOG_FORMAT)) app.logger.addHandler(ch) logger.addHandler(ch) # Log the startup app.logger.info('########################################################') app.logger.info('Starting %s v%s...', config.APP_NAME, config.APP_VERSION) app.logger.info('########################################################') app.logger.debug("Python syspath: %s", sys.path) ########################################################################## # Setup i18n ########################################################################## # Initialise i18n babel = Babel(app) app.logger.debug('Available translations: %s' % babel.list_translations()) @babel.localeselector def get_locale(): """Get the language for the user.""" language = 'en' if config.SERVER_MODE is False: # Get the user language preference from the miscellaneous module if current_user.is_authenticated: user_id = current_user.id else: user = user_datastore.get_user(config.DESKTOP_USER) if user is not None: user_id = user.id user_language = Preferences.raw_value('misc', 'user_language', 'user_language', user_id) if user_language is not None: language = user_language else: # If language is available in get request then return the same # otherwise check the session or cookie data = request.form if 'language' in data: language = data['language'] or language setattr(session, 'PGADMIN_LANGUAGE', language) elif hasattr(session, 'PGADMIN_LANGUAGE'): language = getattr(session, 'PGADMIN_LANGUAGE', language) elif hasattr(request.cookies, 'PGADMIN_LANGUAGE'): language = getattr(request.cookies, 'PGADMIN_LANGUAGE', language) return language ########################################################################## # Setup authentication ########################################################################## app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///{0}?timeout={1}' \ .format(config.SQLITE_PATH.replace('\\', '/'), getattr(config, 'SQLITE_TIMEOUT', 500) ) # Override USER_DOES_NOT_EXIST and INVALID_PASSWORD messages from flask. app.config['SECURITY_MSG_USER_DOES_NOT_EXIST'] = \ app.config['SECURITY_MSG_INVALID_PASSWORD'] = \ (gettext("Incorrect username or password."), "error") # Create database connection object and mailer db.init_app(app) ########################################################################## # Upgrade the schema (if required) ########################################################################## with app.app_context(): # Run migration for the first time i.e. create database from config import SQLITE_PATH from pgadmin.setup import db_upgrade # If version not available, user must have aborted. Tables are not # created and so its an empty db if not os.path.exists(SQLITE_PATH) or get_version() == -1: # If running in cli mode then don't try to upgrade, just raise # the exception if not cli_mode: db_upgrade(app) else: if not os.path.exists(SQLITE_PATH): raise FileNotFoundError('SQLite database file "' + SQLITE_PATH + '" does not exists.') raise RuntimeError('Specified SQLite database file ' 'is not valid.') else: schema_version = get_version() # Run migration if current schema version is greater than the # schema version stored in version table if CURRENT_SCHEMA_VERSION >= schema_version: db_upgrade(app) # Update schema version to the latest if CURRENT_SCHEMA_VERSION > schema_version: set_version(CURRENT_SCHEMA_VERSION) db.session.commit() if os.name != 'nt': os.chmod(config.SQLITE_PATH, 0o600) Mail(app) # Don't bother paths when running in cli mode if not cli_mode: import pgadmin.utils.paths as paths paths.init_app(app) # Setup Flask-Security user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(None, user_datastore) ########################################################################## # Setup security ########################################################################## with app.app_context(): config.CSRF_SESSION_KEY = Keys.query.filter_by( name='CSRF_SESSION_KEY').first().value config.SECRET_KEY = Keys.query.filter_by( name='SECRET_KEY').first().value config.SECURITY_PASSWORD_SALT = Keys.query.filter_by( name='SECURITY_PASSWORD_SALT').first().value # Update the app.config with proper security keyes for signing CSRF data, # signing cookies, and the SALT for hashing the passwords. app.config.update( dict({ 'CSRF_SESSION_KEY': config.CSRF_SESSION_KEY, 'SECRET_KEY': config.SECRET_KEY, 'SECURITY_PASSWORD_SALT': config.SECURITY_PASSWORD_SALT, 'SESSION_COOKIE_DOMAIN': config.SESSION_COOKIE_DOMAIN, # CSRF Token expiration till session expires 'WTF_CSRF_TIME_LIMIT': getattr(config, 'CSRF_TIME_LIMIT', None), 'WTF_CSRF_METHODS': ['GET', 'POST', 'PUT', 'DELETE'], })) security.init_app(app, user_datastore) # register custom unauthorised handler. app.login_manager.unauthorized_handler(pga_unauthorised) # Set the permanent session lifetime to the specified value in config file. app.permanent_session_lifetime = timedelta( days=config.SESSION_EXPIRATION_TIME) if not cli_mode: app.session_interface = create_session_interface( app, config.SESSION_SKIP_PATHS) # Make the Session more secure against XSS & CSRF when running in web mode if config.SERVER_MODE and config.ENHANCED_COOKIE_PROTECTION: paranoid = Paranoid(app) paranoid.redirect_view = 'browser.index' ########################################################################## # Load all available server drivers ########################################################################## driver.init_app(app) authenticate.init_app(app) ########################################################################## # Register language to the preferences after login ########################################################################## @user_logged_in.connect_via(app) def register_language(sender, user): # After logged in, set the language in the preferences if we get from # the login page data = request.form if 'language' in data: language = data['language'] # Set the user language preference misc_preference = Preferences.module('misc') user_languages = misc_preference.preference('user_language') if user_languages and language: language = user_languages.set(language) ########################################################################## # Register any local servers we can discover ########################################################################## @user_logged_in.connect_via(app) def on_user_logged_in(sender, user): # Keep hold of the user ID user_id = user.id # Get the first server group for the user servergroup_id = 1 servergroups = ServerGroup.query.filter_by( user_id=user_id).order_by("id") if servergroups.count() > 0: servergroup = servergroups.first() servergroup_id = servergroup.id '''Add a server to the config database''' def add_server(user_id, servergroup_id, name, superuser, port, discovery_id, comment): # Create a server object if needed, and store it. servers = Server.query.filter_by( user_id=user_id, discovery_id=svr_discovery_id).order_by("id") if servers.count() > 0: return svr = Server(user_id=user_id, servergroup_id=servergroup_id, name=name, host='localhost', port=port, maintenance_db='postgres', username=superuser, ssl_mode='prefer', comment=svr_comment, discovery_id=discovery_id) db.session.add(svr) db.session.commit() # Figure out what servers are present if winreg is not None: arch_keys = set() proc_arch = os.environ['PROCESSOR_ARCHITECTURE'].lower() try: proc_arch64 = os.environ['PROCESSOR_ARCHITEW6432'].lower() except Exception: proc_arch64 = None if proc_arch == 'x86' and not proc_arch64: arch_keys.add(0) elif proc_arch == 'x86' or proc_arch == 'amd64': arch_keys.add(winreg.KEY_WOW64_32KEY) arch_keys.add(winreg.KEY_WOW64_64KEY) for arch_key in arch_keys: for server_type in ('PostgreSQL', 'EnterpriseDB'): try: root_key = winreg.OpenKey( winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\" + server_type + "\\Services", 0, winreg.KEY_READ | arch_key) for i in range(0, winreg.QueryInfoKey(root_key)[0]): inst_id = winreg.EnumKey(root_key, i) inst_key = winreg.OpenKey(root_key, inst_id) svr_name = winreg.QueryValueEx( inst_key, 'Display Name')[0] svr_superuser = winreg.QueryValueEx( inst_key, 'Database Superuser')[0] svr_port = winreg.QueryValueEx(inst_key, 'Port')[0] svr_discovery_id = inst_id svr_comment = gettext( "Auto-detected {0} installation with the data " "directory at {1}").format( winreg.QueryValueEx( inst_key, 'Display Name')[0], winreg.QueryValueEx( inst_key, 'Data Directory')[0]) add_server(user_id, servergroup_id, svr_name, svr_superuser, svr_port, svr_discovery_id, svr_comment) inst_key.Close() except Exception: pass else: # We use the postgres-winreg.ini file on non-Windows from configparser import ConfigParser registry = ConfigParser() try: registry.read('/etc/postgres-reg.ini') sections = registry.sections() # Loop the sections, and get the data from any that are PG or PPAS for section in sections: if (section.startswith('PostgreSQL/') or section.startswith('EnterpriseDB/')): svr_name = registry.get(section, 'Description') svr_superuser = registry.get(section, 'Superuser') # getint function throws exception if value is blank. # Ex: Port= # In such case we should handle the exception and continue # to read the next section of the config file. try: svr_port = registry.getint(section, 'Port') except ValueError: continue svr_discovery_id = section description = registry.get(section, 'Description') data_directory = registry.get(section, 'DataDirectory') svr_comment = gettext( "Auto-detected {0} installation " "with the data directory at {1}").format( description, data_directory) add_server(user_id, servergroup_id, svr_name, svr_superuser, svr_port, svr_discovery_id, svr_comment) except Exception: pass @user_logged_in.connect_via(app) @user_logged_out.connect_via(app) def force_session_write(app, user): session.force_write = True @user_logged_in.connect_via(app) def store_crypt_key(app, user): # in desktop mode, master password is used to encrypt/decrypt # and is stored in the keyManager memory if config.SERVER_MODE and 'password' in request.form: current_app.keyManager.set(request.form['password']) @user_logged_out.connect_via(app) def current_user_cleanup(app, user): from config import PG_DEFAULT_DRIVER from pgadmin.utils.driver import get_driver from flask import current_app # remove key current_app.keyManager.reset() for mdl in current_app.logout_hooks: try: mdl.on_logout(user) except Exception as e: current_app.logger.exception(e) _driver = get_driver(PG_DEFAULT_DRIVER) _driver.gc_own() ########################################################################## # Load plugin modules ########################################################################## for module in app.find_submodules('pgadmin'): app.logger.info('Registering blueprint module: %s' % module) app.register_blueprint(module) app.register_logout_hook(module) @app.before_request def limit_host_addr(): """ This function validate the hosts from ALLOWED_HOSTS before allowing HTTP request to avoid Host Header Injection attack :return: None/JSON response with 403 HTTP status code """ client_host = str(request.host).split(':')[0] valid = True allowed_hosts = config.ALLOWED_HOSTS if len(allowed_hosts) != 0: regex = re.compile( r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?:/\d{1,2}|)') # Create separate list for ip addresses and host names ip_set = list(filter(lambda ip: regex.match(ip), allowed_hosts)) host_set = list( filter(lambda ip: not regex.match(ip), allowed_hosts)) is_ip = regex.match(client_host) if is_ip: ip_address = [] for ip in ip_set: ip_address.extend(list(ipaddress.ip_network(ip))) valid = ip_address.__contains__( ipaddress.ip_address(client_host)) else: valid = host_set.__contains__(client_host) if not valid: return make_json_response(status=403, success=0, errormsg=_("403 FORBIDDEN")) ########################################################################## # Handle the desktop login ########################################################################## @app.before_request def before_request(): """Login the default user if running in desktop mode""" # Check the auth key is valid, if it's set, and we're not in server # mode, and it's not a help file request. if not config.SERVER_MODE and app.PGADMIN_INT_KEY != '' and ( ('key' not in request.args or request.args['key'] != app.PGADMIN_INT_KEY) and request.cookies.get('PGADMIN_INT_KEY') != app.PGADMIN_INT_KEY and request.endpoint != 'help.static'): abort(401) if not config.SERVER_MODE and not current_user.is_authenticated: user = user_datastore.get_user(config.DESKTOP_USER) # Throw an error if we failed to find the desktop user, to give # the sysadmin a hint. We'll continue to try to login anyway as # that'll through a nice 500 error for us. if user is None: app.logger.error( 'The desktop user %s was not found in the configuration ' 'database.' % config.DESKTOP_USER) abort(401) login_user(user) # if the server is restarted the in memory key will be lost # but the user session may still be active. Logout the user # to get the key again when login if config.SERVER_MODE and current_user.is_authenticated and \ current_app.keyManager.get() is None and \ request.endpoint not in ('security.login', 'security.logout'): logout_user() @app.after_request def after_request(response): if 'key' in request.args: domain = dict() if config.COOKIE_DEFAULT_DOMAIN and \ config.COOKIE_DEFAULT_DOMAIN != 'localhost': domain['domain'] = config.COOKIE_DEFAULT_DOMAIN response.set_cookie('PGADMIN_INT_KEY', value=request.args['key'], path=config.COOKIE_DEFAULT_PATH, secure=config.SESSION_COOKIE_SECURE, httponly=config.SESSION_COOKIE_HTTPONLY, samesite=config.SESSION_COOKIE_SAMESITE, **domain) SecurityHeaders.set_response_headers(response) return response ########################################################################## # Cache busting ########################################################################## # Version number to be added to all static file url requests # This is used by url_for function when generating urls # This will solve caching issues when application is upgrading # This is called - Cache Busting @app.url_defaults def add_internal_version(endpoint, values): extensions = config.APP_VERSION_EXTN # Add the internal version only if it is set if config.APP_VERSION_PARAM is not None and \ config.APP_VERSION_PARAM != '': # If there is a filename, add the version if 'filename' in values \ and values['filename'].endswith(extensions): values[config.APP_VERSION_PARAM] = config.APP_VERSION_INT else: # Sometimes there may be direct endpoint for some files # There will be only one rule for such endpoints urls = [url for url in app.url_map.iter_rules(endpoint)] if len(urls) == 1 and urls[0].rule.endswith(extensions): values[config.APP_VERSION_PARAM] = \ config.APP_VERSION_INT # Strip away internal version param before sending further to app as it was # required for cache busting only @app.url_value_preprocessor def strip_version_number(endpoint, values): if values and config.APP_VERSION_PARAM in values: values.pop(config.APP_VERSION_PARAM) ########################################################################## # Minify output. Not required in desktop mode ########################################################################## if not config.DEBUG and config.SERVER_MODE: from flask_compress import Compress Compress(app) from pgadmin.misc.themes import themes themes(app) @app.context_processor def inject_blueprint(): """ Inject a reference to the current blueprint, if any. """ return { 'current_app': current_app, 'current_blueprint': current_blueprint, } @app.errorhandler(Exception) def all_exception_handler(e): current_app.logger.error(e, exc_info=True) return internal_server_error(errormsg=str(e)) # Exclude HTTPexception from above handler (all_exception_handler) # HTTPException are user defined exceptions and those should be returned # as is @app.errorhandler(HTTPException) def http_exception_handler(e): current_app.logger.error(e, exc_info=True) return e # Intialize the key manager app.keyManager = KeyManager() ########################################################################## # Protection against CSRF attacks ########################################################################## with app.app_context(): pgCSRFProtect.init_app(app) ########################################################################## # All done! ########################################################################## return app
def test_access_datastore_from_factory(app, datastore): security = Security() security.init_app(app, datastore) assert security.datastore is not None assert security.app is not None
class InvenioAccounts(object): """Invenio-Accounts extension.""" def __init__(self, app=None): """Extension initialization.""" self.security = Security() self.datastore = None if app: self.init_app(app) def init_app(self, app, use_celery=True): """Flask application initialization.""" self.init_config(app) # Create user datastore self.datastore = SQLAlchemyUserDatastore(db, User, Role) # Initialize extension. state = self.security.init_app(app, datastore=self.datastore) if app.config['ACCOUNTS_USE_CELERY']: from invenio_accounts.tasks import send_security_email @state.send_mail_task def delay_security_email(msg): send_security_email.delay(msg) # Register CLI app.cli.add_command(roles_cli, 'roles') app.cli.add_command(users_cli, 'users') app.extensions['invenio-accounts'] = self def init_config(self, app): """Initialize configuration.""" try: pkg_resources.get_distribution('celery') app.config.setdefault("ACCOUNTS_USE_CELERY", True) except pkg_resources.DistributionNotFound: app.config.setdefault("ACCOUNTS_USE_CELERY", False) app.config.setdefault('ACCOUNTS', True) # Change Flask-Security defaults app.config.setdefault('SECURITY_CHANGEABLE', True) app.config.setdefault('SECURITY_CONFIRMABLE', True) app.config.setdefault('SECURITY_PASSWORD_HASH', 'pbkdf2_sha512') app.config.setdefault('SECURITY_PASSWORD_SCHEMES', ['pbkdf2_sha512']) app.config.setdefault('SECURITY_DEPRECATED_PASSWORD_SCHEMES', []) app.config.setdefault('SECURITY_RECOVERABLE', True) app.config.setdefault('SECURITY_REGISTERABLE', True) app.config.setdefault('SECURITY_TRACKABLE', True) app.config.setdefault('SECURITY_LOGIN_WITHOUT_CONFIRMATION', True) app.config.setdefault('SECURITY_PASSWORD_SALT', app.config['SECRET_KEY']) # Change default templates app.config.setdefault("SECURITY_FORGOT_PASSWORD_TEMPLATE", "invenio_accounts/forgot_password.html") app.config.setdefault("SECURITY_LOGIN_USER_TEMPLATE", "invenio_accounts/login_user.html") app.config.setdefault("SECURITY_REGISTER_USER_TEMPLATE", "invenio_accounts/register_user.html") app.config.setdefault("SECURITY_RESET_PASSWORD_TEMPLATE", "invenio_accounts/reset_password.html") app.config.setdefault("SECURITY_CHANGE_PASSWORD_TEMPLATE", "invenio_accounts/change_password.html") app.config.setdefault("SECURITY_SEND_CONFIRMATION_TEMPLATE", "invenio_accounts/send_confirmation.html") app.config.setdefault("SECURITY_SEND_LOGIN_TEMPLATE", "invenio_accounts/send_login.html") app.config.setdefault("SECURITY_REGISTER_URL", "/signup")
url_base_pathname="/", update_title=None) app.title = "MiCV" app.config.suppress_callback_exceptions = True # Setup cache cache = Cache(app.server, config={ 'CACHE_TYPE': 'redis', 'CACHE_THRESHOLD': 200, # should be set to max number of active users "CACHE_DEFAULT_TIMEOUT": 30000 } ) # Setup Flask-Security user_datastore = SQLAlchemySessionUserDatastore(db_session, User, Role) security_ctx = security.init_app(app.server, user_datastore, register_blueprint=True) def delay_flask_security_mail(msg): print("[DEBUG] running delay_flask_security_mail") with app.server.app_context(): send_flask_mail.delay(subject=msg.subject, sender=msg.sender, recipients=msg.recipients, body=msg.body, html=msg.html) return None security_ctx.send_mail_task(delay_flask_security_mail) # Create a user to test with @server.before_first_request def initialize_database(): init_db()
def test_access_datastore_from_app_factory_pattern(app, datastore): security = Security(datastore=datastore) security.init_app(app) assert security.datastore is not None assert security.app is not None
def create_app(app_name=None): # Configuration settings import config if not app_name: app_name = config.APP_NAME # Only enable password related functionality in server mode. if config.SERVER_MODE is True: # Some times we need to access these config params where application # context is not available (we can't use current_app.config in those # cases even with current_app.app_context()) # So update these params in config itself. # And also these updated config values will picked up by application # since we are updating config before the application instance is # created. config.SECURITY_RECOVERABLE = True config.SECURITY_CHANGEABLE = True # Now we'll open change password page in alertify dialog # we don't want it to redirect to main page after password # change operation so we will open the same password change page again. config.SECURITY_POST_CHANGE_VIEW = 'browser.change_password' """Create the Flask application, startup logging and dynamically load additional modules (blueprints) that are found in this directory.""" app = PgAdmin(__name__, static_url_path='/static') # Removes unwanted whitespace from render_template function app.jinja_env.trim_blocks = True app.config.from_object(config) app.config.update(dict(PROPAGATE_EXCEPTIONS=True)) ########################################################################## # Setup logging and log the application startup ########################################################################## # Add SQL level logging, and set the base logging level logging.addLevelName(25, 'SQL') app.logger.setLevel(logging.DEBUG) app.logger.handlers = [] # We also need to update the handler on the webserver in order to see # request. Setting the level prevents werkzeug from setting up it's own # stream handler thus ensuring all the logging goes through the pgAdmin # logger. logger = logging.getLogger('werkzeug') logger.setLevel(logging.INFO) # Set SQLITE_PATH to TEST_SQLITE_PATH while running test cases if ( 'PGADMIN_TESTING_MODE' in os.environ and os.environ['PGADMIN_TESTING_MODE'] == '1' ): config.SQLITE_PATH = config.TEST_SQLITE_PATH # Ensure the various working directories exist from pgadmin.setup import create_app_data_directory, db_upgrade create_app_data_directory(config) # File logging fh = logging.FileHandler(config.LOG_FILE, encoding='utf-8') fh.setLevel(config.FILE_LOG_LEVEL) fh.setFormatter(logging.Formatter(config.FILE_LOG_FORMAT)) app.logger.addHandler(fh) logger.addHandler(fh) # Console logging ch = logging.StreamHandler() ch.setLevel(config.CONSOLE_LOG_LEVEL) ch.setFormatter(logging.Formatter(config.CONSOLE_LOG_FORMAT)) app.logger.addHandler(ch) logger.addHandler(ch) # Log the startup app.logger.info('########################################################') app.logger.info('Starting %s v%s...', config.APP_NAME, config.APP_VERSION) app.logger.info('########################################################') app.logger.debug("Python syspath: %s", sys.path) ########################################################################## # Setup i18n ########################################################################## # Initialise i18n babel = Babel(app) app.logger.debug('Available translations: %s' % babel.list_translations()) @babel.localeselector def get_locale(): """Get the language for the user.""" language = 'en' if config.SERVER_MODE is False: # Get the user language preference from the miscellaneous module if current_user.is_authenticated: user_id = current_user.id else: user = user_datastore.get_user(config.DESKTOP_USER) if user is not None: user_id = user.id user_language = Preferences.raw_value( 'miscellaneous', 'user_language', None, user_id ) if user_language is not None: language = user_language else: # If language is available in get request then return the same # otherwise check the session or cookie data = request.form if 'language' in data: language = data['language'] or language setattr(session, 'PGADMIN_LANGUAGE', language) elif hasattr(session, 'PGADMIN_LANGUAGE'): language = getattr(session, 'PGADMIN_LANGUAGE', language) elif hasattr(request.cookies, 'PGADMIN_LANGUAGE'): language = getattr( request.cookies, 'PGADMIN_LANGUAGE', language ) return language ########################################################################## # Setup authentication ########################################################################## app.config['SQLALCHEMY_DATABASE_URI'] = u'sqlite:///{0}?timeout={1}' \ .format(config.SQLITE_PATH.replace(u'\\', u'/'), getattr(config, 'SQLITE_TIMEOUT', 500) ) # Create database connection object and mailer db.init_app(app) ########################################################################## # Upgrade the schema (if required) ########################################################################## with app.app_context(): # Run migration for the first time i.e. create database from config import SQLITE_PATH if not os.path.exists(SQLITE_PATH): db_upgrade(app) else: version = Version.query.filter_by(name='ConfigDB').first() schema_version = version.value # Run migration if current schema version is greater than the # schema version stored in version table if CURRENT_SCHEMA_VERSION >= schema_version: db_upgrade(app) # Update schema version to the latest if CURRENT_SCHEMA_VERSION > schema_version: version = Version.query.filter_by(name='ConfigDB').first() version.value = CURRENT_SCHEMA_VERSION db.session.commit() Mail(app) import pgadmin.utils.paths as paths paths.init_app(app) # Setup Flask-Security user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(None, user_datastore) ########################################################################## # Setup security ########################################################################## with app.app_context(): config.CSRF_SESSION_KEY = Keys.query.filter_by( name='CSRF_SESSION_KEY').first().value config.SECRET_KEY = Keys.query.filter_by( name='SECRET_KEY').first().value config.SECURITY_PASSWORD_SALT = Keys.query.filter_by( name='SECURITY_PASSWORD_SALT').first().value # Update the app.config with proper security keyes for signing CSRF data, # signing cookies, and the SALT for hashing the passwords. app.config.update(dict({ 'CSRF_SESSION_KEY': config.CSRF_SESSION_KEY, 'SECRET_KEY': config.SECRET_KEY, 'SECURITY_PASSWORD_SALT': config.SECURITY_PASSWORD_SALT, 'SESSION_COOKIE_DOMAIN': config.SESSION_COOKIE_DOMAIN })) security.init_app(app, user_datastore) # register custom unauthorised handler. app.login_manager.unauthorized_handler(pga_unauthorised) # Set the permanent session lifetime to the specified value in config file. app.permanent_session_lifetime = timedelta( days=config.SESSION_EXPIRATION_TIME) app.session_interface = create_session_interface( app, config.SESSION_SKIP_PATHS ) # Make the Session more secure against XSS & CSRF when running in web mode if config.SERVER_MODE: paranoid = Paranoid(app) paranoid.redirect_view = 'browser.index' ########################################################################## # Load all available server drivers ########################################################################## driver.init_app(app) ########################################################################## # Register language to the preferences after login ########################################################################## @user_logged_in.connect_via(app) def register_language(sender, user): # After logged in, set the language in the preferences if we get from # the login page data = request.form if 'language' in data: language = data['language'] # Set the user language preference misc_preference = Preferences.module('miscellaneous') user_languages = misc_preference.preference( 'user_language' ) if user_languages and language: language = user_languages.set(language) ########################################################################## # Register any local servers we can discover ########################################################################## @user_logged_in.connect_via(app) def on_user_logged_in(sender, user): # Keep hold of the user ID user_id = user.id # Get the first server group for the user servergroup_id = 1 servergroups = ServerGroup.query.filter_by( user_id=user_id ).order_by("id") if servergroups.count() > 0: servergroup = servergroups.first() servergroup_id = servergroup.id '''Add a server to the config database''' def add_server(user_id, servergroup_id, name, superuser, port, discovery_id, comment): # Create a server object if needed, and store it. servers = Server.query.filter_by( user_id=user_id, discovery_id=svr_discovery_id ).order_by("id") if servers.count() > 0: return svr = Server(user_id=user_id, servergroup_id=servergroup_id, name=name, host='localhost', port=port, maintenance_db='postgres', username=superuser, ssl_mode='prefer', comment=svr_comment, discovery_id=discovery_id) db.session.add(svr) db.session.commit() # Figure out what servers are present if winreg is not None: arch_keys = set() proc_arch = os.environ['PROCESSOR_ARCHITECTURE'].lower() try: proc_arch64 = os.environ['PROCESSOR_ARCHITEW6432'].lower() except Exception as e: proc_arch64 = None if proc_arch == 'x86' and not proc_arch64: arch_keys.add(0) elif proc_arch == 'x86' or proc_arch == 'amd64': arch_keys.add(winreg.KEY_WOW64_32KEY) arch_keys.add(winreg.KEY_WOW64_64KEY) for arch_key in arch_keys: for server_type in ('PostgreSQL', 'EnterpriseDB'): try: root_key = winreg.OpenKey( winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\" + server_type + "\Services", 0, winreg.KEY_READ | arch_key ) for i in xrange(0, winreg.QueryInfoKey(root_key)[0]): inst_id = winreg.EnumKey(root_key, i) inst_key = winreg.OpenKey(root_key, inst_id) svr_name = winreg.QueryValueEx( inst_key, 'Display Name' )[0] svr_superuser = winreg.QueryValueEx( inst_key, 'Database Superuser' )[0] svr_port = winreg.QueryValueEx(inst_key, 'Port')[0] svr_discovery_id = inst_id svr_comment = gettext( "Auto-detected %s installation with the data " "directory at %s" % ( winreg.QueryValueEx( inst_key, 'Display Name' )[0], winreg.QueryValueEx( inst_key, 'Data Directory' )[0] ) ) add_server( user_id, servergroup_id, svr_name, svr_superuser, svr_port, svr_discovery_id, svr_comment ) inst_key.Close() except Exception as e: pass else: # We use the postgres-winreg.ini file on non-Windows try: from configparser import ConfigParser except ImportError: from ConfigParser import ConfigParser # Python 2 registry = ConfigParser() try: registry.read('/etc/postgres-reg.ini') sections = registry.sections() # Loop the sections, and get the data from any that are PG or PPAS for section in sections: if ( section.startswith('PostgreSQL/') or section.startswith('EnterpriseDB/') ): svr_name = registry.get(section, 'Description') svr_superuser = registry.get(section, 'Superuser') # getint function throws exception if value is blank. # Ex: Port= # In such case we should handle the exception and continue # to read the next section of the config file. try: svr_port = registry.getint(section, 'Port') except ValueError: continue svr_discovery_id = section description = registry.get(section, 'Description') data_directory = registry.get(section, 'DataDirectory') if hasattr(str, 'decode'): description = description.decode('utf-8') data_directory = data_directory.decode('utf-8') svr_comment = gettext(u"Auto-detected %s installation " u"with the data directory at %s" % ( description, data_directory ) ) add_server(user_id, servergroup_id, svr_name, svr_superuser, svr_port, svr_discovery_id, svr_comment) except Exception as e: pass @user_logged_in.connect_via(app) @user_logged_out.connect_via(app) def force_session_write(app, user): session.force_write = True @user_logged_out.connect_via(app) def clear_current_user_connections(app, user): from config import PG_DEFAULT_DRIVER from pgadmin.utils.driver import get_driver _driver = get_driver(PG_DEFAULT_DRIVER) _driver.gc_own() ########################################################################## # Load plugin modules ########################################################################## for module in app.find_submodules('pgadmin'): app.logger.info('Registering blueprint module: %s' % module) app.register_blueprint(module) ########################################################################## # Handle the desktop login ########################################################################## @app.before_request def before_request(): """Login the default user if running in desktop mode""" # Check the auth key is valid, if it's set, and we're not in server # mode, and it's not a help file request. if not config.SERVER_MODE and app.PGADMIN_KEY != '': if ( ('key' not in request.args or request.args['key'] != app.PGADMIN_KEY) and request.cookies.get('PGADMIN_KEY') != app.PGADMIN_KEY and request.endpoint != 'help.static' ): abort(401) if not config.SERVER_MODE and not current_user.is_authenticated: user = user_datastore.get_user(config.DESKTOP_USER) # Throw an error if we failed to find the desktop user, to give # the sysadmin a hint. We'll continue to try to login anyway as # that'll through a nice 500 error for us. if user is None: app.logger.error( 'The desktop user %s was not found in the configuration ' 'database.' % config.DESKTOP_USER ) abort(401) login_user(user) @app.after_request def after_request(response): if 'key' in request.args: domain = dict() if config.COOKIE_DEFAULT_DOMAIN and \ config.COOKIE_DEFAULT_DOMAIN != 'localhost': domain['domain'] = config.COOKIE_DEFAULT_DOMAIN response.set_cookie('PGADMIN_KEY', value=request.args['key'], path=config.COOKIE_DEFAULT_PATH, **domain) return response ########################################################################## # Cache busting ########################################################################## # Version number to be added to all static file url requests # This is used by url_for function when generating urls # This will solve caching issues when application is upgrading # This is called - Cache Busting @app.url_defaults def add_internal_version(endpoint, values): extensions = config.APP_VERSION_EXTN # Add the internal version only if it is set if config.APP_VERSION_PARAM is not None and \ config.APP_VERSION_PARAM != '': # If there is a filename, add the version if 'filename' in values \ and values['filename'].endswith(extensions): values[config.APP_VERSION_PARAM] = config.APP_VERSION_INT else: # Sometimes there may be direct endpoint for some files # There will be only one rule for such endpoints urls = [url for url in app.url_map.iter_rules(endpoint)] if len(urls) == 1 and urls[0].rule.endswith(extensions): values[config.APP_VERSION_PARAM] = \ config.APP_VERSION_INT # Strip away internal version param before sending further to app as it was # required for cache busting only @app.url_value_preprocessor def strip_version_number(endpoint, values): if values and config.APP_VERSION_PARAM in values: values.pop(config.APP_VERSION_PARAM) ########################################################################## # Minify output ########################################################################## # HTMLMIN doesn't work with Python 2.6. if not config.DEBUG and sys.version_info >= (2, 7): from flask_htmlmin import HTMLMIN HTMLMIN(app) @app.context_processor def inject_blueprint(): """Inject a reference to the current blueprint, if any.""" return { 'current_app': current_app, 'current_blueprint': current_blueprint } ########################################################################## # All done! ########################################################################## return app
def register_security(app): user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security() security.init_app(app, user_datastore) return None
def create_app(app_name=None): # Configuration settings import config if not app_name: app_name = config.APP_NAME # Only enable password related functionality in server mode. if config.SERVER_MODE is True: # Some times we need to access these config params where application # context is not available (we can't use current_app.config in those # cases even with current_app.app_context()) # So update these params in config itself. # And also these updated config values will picked up by application # since we are updating config before the application instance is # created. config.SECURITY_RECOVERABLE = True config.SECURITY_CHANGEABLE = True # Now we'll open change password page in alertify dialog # we don't want it to redirect to main page after password # change operation so we will open the same password change page again. config.SECURITY_POST_CHANGE_VIEW = 'browser.change_password' """Create the Flask application, startup logging and dynamically load additional modules (blueprints) that are found in this directory.""" app = PgAdmin(__name__, static_url_path='/static') # Removes unwanted whitespace from render_template function app.jinja_env.trim_blocks = True app.config.from_object(config) app.config.update(dict(PROPAGATE_EXCEPTIONS=True)) ########################################################################## # Setup logging and log the application startup ########################################################################## # Add SQL level logging, and set the base logging level logging.addLevelName(25, 'SQL') app.logger.setLevel(logging.DEBUG) app.logger.handlers = [] # We also need to update the handler on the webserver in order to see # request. Setting the level prevents werkzeug from setting up it's own # stream handler thus ensuring all the logging goes through the pgAdmin # logger. logger = logging.getLogger('werkzeug') logger.setLevel(logging.INFO) # Set SQLITE_PATH to TEST_SQLITE_PATH while running test cases if ( 'PGADMIN_TESTING_MODE' in os.environ and os.environ['PGADMIN_TESTING_MODE'] == '1' ): config.SQLITE_PATH = config.TEST_SQLITE_PATH # Ensure the various working directories exist from pgadmin.setup import create_app_data_directory, db_upgrade create_app_data_directory(config) # File logging fh = logging.FileHandler(config.LOG_FILE, encoding='utf-8') fh.setLevel(config.FILE_LOG_LEVEL) fh.setFormatter(logging.Formatter(config.FILE_LOG_FORMAT)) app.logger.addHandler(fh) logger.addHandler(fh) # Console logging ch = logging.StreamHandler() ch.setLevel(config.CONSOLE_LOG_LEVEL) ch.setFormatter(logging.Formatter(config.CONSOLE_LOG_FORMAT)) app.logger.addHandler(ch) logger.addHandler(ch) # Log the startup app.logger.info('########################################################') app.logger.info('Starting %s v%s...', config.APP_NAME, config.APP_VERSION) app.logger.info('########################################################') app.logger.debug("Python syspath: %s", sys.path) ########################################################################## # Setup i18n ########################################################################## # Initialise i18n babel = Babel(app) app.logger.debug('Available translations: %s' % babel.list_translations()) @babel.localeselector def get_locale(): """Get the language for the user.""" language = 'en' if config.SERVER_MODE is False: # Get the user language preference from the miscellaneous module if current_user.is_authenticated: user_id = current_user.id else: user = user_datastore.get_user(config.DESKTOP_USER) if user is not None: user_id = user.id user_language = Preferences.raw_value( 'miscellaneous', 'user_language', None, user_id ) if user_language is not None: language = user_language else: # If language is available in get request then return the same # otherwise check the session or cookie data = request.form if 'language' in data: language = data['language'] or language setattr(session, 'PGADMIN_LANGUAGE', language) elif hasattr(session, 'PGADMIN_LANGUAGE'): language = getattr(session, 'PGADMIN_LANGUAGE', language) elif hasattr(request.cookies, 'PGADMIN_LANGUAGE'): language = getattr( request.cookies, 'PGADMIN_LANGUAGE', language ) return language ########################################################################## # Setup authentication ########################################################################## app.config['SQLALCHEMY_DATABASE_URI'] = u'sqlite:///{0}?timeout={1}' \ .format(config.SQLITE_PATH.replace(u'\\', u'/'), getattr(config, 'SQLITE_TIMEOUT', 500) ) # Create database connection object and mailer db.init_app(app) ########################################################################## # Upgrade the schema (if required) ########################################################################## with app.app_context(): # Run migration for the first time i.e. create database from config import SQLITE_PATH if not os.path.exists(SQLITE_PATH): db_upgrade(app) else: version = Version.query.filter_by(name='ConfigDB').first() schema_version = version.value # Run migration if current schema version is greater than the # schema version stored in version table if CURRENT_SCHEMA_VERSION >= schema_version: db_upgrade(app) # Update schema version to the latest if CURRENT_SCHEMA_VERSION > schema_version: version = Version.query.filter_by(name='ConfigDB').first() version.value = CURRENT_SCHEMA_VERSION db.session.commit() Mail(app) import pgadmin.utils.paths as paths paths.init_app(app) # Setup Flask-Security user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(None, user_datastore) ########################################################################## # Setup security ########################################################################## with app.app_context(): config.CSRF_SESSION_KEY = Keys.query.filter_by( name='CSRF_SESSION_KEY').first().value config.SECRET_KEY = Keys.query.filter_by( name='SECRET_KEY').first().value config.SECURITY_PASSWORD_SALT = Keys.query.filter_by( name='SECURITY_PASSWORD_SALT').first().value # Update the app.config with proper security keyes for signing CSRF data, # signing cookies, and the SALT for hashing the passwords. app.config.update(dict({ 'CSRF_SESSION_KEY': config.CSRF_SESSION_KEY, 'SECRET_KEY': config.SECRET_KEY, 'SECURITY_PASSWORD_SALT': config.SECURITY_PASSWORD_SALT, 'SESSION_COOKIE_DOMAIN': config.SESSION_COOKIE_DOMAIN })) security.init_app(app, user_datastore) # register custom unauthorised handler. app.login_manager.unauthorized_handler(pga_unauthorised) app.session_interface = create_session_interface(app) # Make the Session more secure against XSS & CSRF when running in web mode if config.SERVER_MODE: paranoid = Paranoid(app) paranoid.redirect_view = 'browser.index' ########################################################################## # Load all available server drivers ########################################################################## driver.init_app(app) ########################################################################## # Register language to the preferences after login ########################################################################## @user_logged_in.connect_via(app) def register_language(sender, user): # After logged in, set the language in the preferences if we get from # the login page data = request.form if 'language' in data: language = data['language'] # Set the user language preference misc_preference = Preferences.module('miscellaneous') user_languages = misc_preference.preference( 'user_language' ) if user_languages and language: language = user_languages.set(language) ########################################################################## # Register any local servers we can discover ########################################################################## @user_logged_in.connect_via(app) def on_user_logged_in(sender, user): # Keep hold of the user ID user_id = user.id # Get the first server group for the user servergroup_id = 1 servergroups = ServerGroup.query.filter_by( user_id=user_id ).order_by("id") if servergroups.count() > 0: servergroup = servergroups.first() servergroup_id = servergroup.id '''Add a server to the config database''' def add_server(user_id, servergroup_id, name, superuser, port, discovery_id, comment): # Create a server object if needed, and store it. servers = Server.query.filter_by( user_id=user_id, discovery_id=svr_discovery_id ).order_by("id") if servers.count() > 0: return svr = Server(user_id=user_id, servergroup_id=servergroup_id, name=name, host='localhost', port=port, maintenance_db='postgres', username=superuser, ssl_mode='prefer', comment=svr_comment, discovery_id=discovery_id) db.session.add(svr) db.session.commit() # Figure out what servers are present if winreg is not None: arch_keys = set() proc_arch = os.environ['PROCESSOR_ARCHITECTURE'].lower() try: proc_arch64 = os.environ['PROCESSOR_ARCHITEW6432'].lower() except Exception as e: proc_arch64 = None if proc_arch == 'x86' and not proc_arch64: arch_keys.add(0) elif proc_arch == 'x86' or proc_arch == 'amd64': arch_keys.add(winreg.KEY_WOW64_32KEY) arch_keys.add(winreg.KEY_WOW64_64KEY) for arch_key in arch_keys: for server_type in ('PostgreSQL', 'EnterpriseDB'): try: root_key = winreg.OpenKey( winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\" + server_type + "\Services", 0, winreg.KEY_READ | arch_key ) for i in xrange(0, winreg.QueryInfoKey(root_key)[0]): inst_id = winreg.EnumKey(root_key, i) inst_key = winreg.OpenKey(root_key, inst_id) svr_name = winreg.QueryValueEx( inst_key, 'Display Name' )[0] svr_superuser = winreg.QueryValueEx( inst_key, 'Database Superuser' )[0] svr_port = winreg.QueryValueEx(inst_key, 'Port')[0] svr_discovery_id = inst_id svr_comment = gettext( "Auto-detected %s installation with the data " "directory at %s" % ( winreg.QueryValueEx( inst_key, 'Display Name' )[0], winreg.QueryValueEx( inst_key, 'Data Directory' )[0] ) ) add_server( user_id, servergroup_id, svr_name, svr_superuser, svr_port, svr_discovery_id, svr_comment ) inst_key.Close() except Exception as e: pass else: # We use the postgres-winreg.ini file on non-Windows try: from configparser import ConfigParser except ImportError: from ConfigParser import ConfigParser # Python 2 registry = ConfigParser() try: registry.read('/etc/postgres-reg.ini') sections = registry.sections() # Loop the sections, and get the data from any that are PG or PPAS for section in sections: if ( section.startswith('PostgreSQL/') or section.startswith('EnterpriseDB/') ): svr_name = registry.get(section, 'Description') svr_superuser = registry.get(section, 'Superuser') svr_port = registry.getint(section, 'Port') svr_discovery_id = section description = registry.get(section, 'Description') data_directory = registry.get(section, 'DataDirectory') if hasattr(str, 'decode'): description = description.decode('utf-8') data_directory = data_directory.decode('utf-8') svr_comment = gettext(u"Auto-detected %s installation " u"with the data directory at %s" % ( description, data_directory ) ) add_server(user_id, servergroup_id, svr_name, svr_superuser, svr_port, svr_discovery_id, svr_comment) except Exception as e: pass @user_logged_in.connect_via(app) @user_logged_out.connect_via(app) def force_session_write(app, user): session.force_write = True ########################################################################## # Load plugin modules ########################################################################## for module in app.find_submodules('pgadmin'): app.logger.info('Registering blueprint module: %s' % module) app.register_blueprint(module) ########################################################################## # Handle the desktop login ########################################################################## @app.before_request def before_request(): """Login the default user if running in desktop mode""" # Check the auth key is valid, if it's set, and we're not in server # mode, and it's not a help file request. if not config.SERVER_MODE and app.PGADMIN_KEY != '': if ( ('key' not in request.args or request.args['key'] != app.PGADMIN_KEY) and request.cookies.get('PGADMIN_KEY') != app.PGADMIN_KEY and request.endpoint != 'help.static' ): abort(401) if not config.SERVER_MODE and not current_user.is_authenticated: user = user_datastore.get_user(config.DESKTOP_USER) # Throw an error if we failed to find the desktop user, to give # the sysadmin a hint. We'll continue to try to login anyway as # that'll through a nice 500 error for us. if user is None: app.logger.error( 'The desktop user %s was not found in the configuration ' 'database.' % config.DESKTOP_USER ) abort(401) login_user(user) @app.after_request def after_request(response): if 'key' in request.args: domain = dict() if config.COOKIE_DEFAULT_DOMAIN and \ config.COOKIE_DEFAULT_DOMAIN != 'localhost': domain['domain'] = config.COOKIE_DEFAULT_DOMAIN response.set_cookie('PGADMIN_KEY', value=request.args['key'], path=config.COOKIE_DEFAULT_PATH, **domain) return response ########################################################################## # Minify output ########################################################################## # HTMLMIN doesn't work with Python 2.6. if not config.DEBUG and sys.version_info >= (2, 7): from flask_htmlmin import HTMLMIN HTMLMIN(app) @app.context_processor def inject_blueprint(): """Inject a reference to the current blueprint, if any.""" return { 'current_app': current_app, 'current_blueprint': current_blueprint } ########################################################################## # All done! ########################################################################## return app
class InvenioAccounts(object): """Invenio-Accounts extension.""" def __init__(self, app=None, sessionstore=None): """Extension initialization.""" self.security = Security() self.datastore = None if app: self.init_app(app, sessionstore=sessionstore) def init_app(self, app, use_celery=True, sessionstore=None): """Flask application initialization. :param sessionstore: store for sessions. Passed to ``flask-kvsession``. Defaults to redis. """ self.init_config(app) # Create user datastore self.datastore = SQLAlchemyUserDatastore(db, User, Role) # Create sessionstore if sessionstore is None: import redis from simplekv.memory.redisstore import RedisStore sessionstore = RedisStore(redis.StrictRedis.from_url( app.config['ACCOUNTS_SESSION_REDIS_URL'])) self.sessionstore = sessionstore user_logged_in.connect(login_listener, app) # Initialize extension. state = self.security.init_app(app, datastore=self.datastore) self.kvsession_extension = KVSessionExtension(self.sessionstore, app) if app.config['ACCOUNTS_USE_CELERY']: from invenio_accounts.tasks import send_security_email @state.send_mail_task def delay_security_email(msg): send_security_email.delay(msg.__dict__) # Register CLI app.cli.add_command(roles_cli, 'roles') app.cli.add_command(users_cli, 'users') app.extensions['invenio-accounts'] = self def init_config(self, app): """Initialize configuration.""" try: pkg_resources.get_distribution('celery') app.config.setdefault( "ACCOUNTS_USE_CELERY", not (app.debug or app.testing)) except pkg_resources.DistributionNotFound: app.config.setdefault("ACCOUNTS_USE_CELERY", False) app.config.setdefault('ACCOUNTS', True) # Change Flask-Security defaults app.config.setdefault('SECURITY_CHANGEABLE', True) app.config.setdefault('SECURITY_CONFIRMABLE', True) app.config.setdefault('SECURITY_PASSWORD_HASH', 'pbkdf2_sha512') app.config.setdefault('SECURITY_PASSWORD_SCHEMES', ['pbkdf2_sha512']) app.config.setdefault('SECURITY_DEPRECATED_PASSWORD_SCHEMES', []) app.config.setdefault('SECURITY_RECOVERABLE', True) app.config.setdefault('SECURITY_REGISTERABLE', True) app.config.setdefault('SECURITY_TRACKABLE', True) app.config.setdefault('SECURITY_LOGIN_WITHOUT_CONFIRMATION', True) app.config.setdefault('SECURITY_PASSWORD_SALT', app.config['SECRET_KEY']) # Change default templates app.config.setdefault("SECURITY_FORGOT_PASSWORD_TEMPLATE", "invenio_accounts/forgot_password.html") app.config.setdefault("SECURITY_LOGIN_USER_TEMPLATE", "invenio_accounts/login_user.html") app.config.setdefault("SECURITY_REGISTER_USER_TEMPLATE", "invenio_accounts/register_user.html") app.config.setdefault("SECURITY_RESET_PASSWORD_TEMPLATE", "invenio_accounts/reset_password.html") app.config.setdefault("SECURITY_CHANGE_PASSWORD_TEMPLATE", "invenio_accounts/change_password.html") app.config.setdefault("SECURITY_SEND_CONFIRMATION_TEMPLATE", "invenio_accounts/send_confirmation.html") app.config.setdefault("SECURITY_SEND_LOGIN_TEMPLATE", "invenio_accounts/send_login.html") app.config.setdefault("SECURITY_REGISTER_URL", "/signup/") app.config.setdefault("SECURITY_RESET_URL", "/lost-password/") app.config.setdefault("SECURITY_LOGIN_URL", "/login/") app.config.setdefault("SECURITY_LOGOUT_URL", "/logout/") app.config.setdefault("SECURITY_CHANGE_URL", "/accounts/settings/password/") for k in dir(config): if k.startswith('ACCOUNTS_'): app.config.setdefault(k, getattr(config, k))
def create_app(app_name=None): # Configuration settings import config if not app_name: app_name = config.APP_NAME """Create the Flask application, startup logging and dynamically load additional modules (blueprints) that are found in this directory.""" app = PgAdmin(__name__, static_url_path='/static') # Removes unwanted whitespace from render_template function app.jinja_env.trim_blocks = True app.config.from_object(config) app.config.update(dict(PROPAGATE_EXCEPTIONS=True)) ########################################################################## # Setup logging and log the application startup ########################################################################## # Add SQL level logging, and set the base logging level logging.addLevelName(25, 'SQL') app.logger.setLevel(logging.DEBUG) app.logger.handlers = [] # We also need to update the handler on the webserver in order to see # request. Setting the level prevents werkzeug from setting up it's own # stream handler thus ensuring all the logging goes through the pgAdmin # logger. logger = logging.getLogger('werkzeug') logger.setLevel(logging.INFO) # File logging fh = logging.FileHandler(config.LOG_FILE, encoding='utf-8') fh.setLevel(config.FILE_LOG_LEVEL) fh.setFormatter(logging.Formatter(config.FILE_LOG_FORMAT)) app.logger.addHandler(fh) logger.addHandler(fh) # Console logging ch = logging.StreamHandler() ch.setLevel(config.CONSOLE_LOG_LEVEL) ch.setFormatter(logging.Formatter(config.CONSOLE_LOG_FORMAT)) app.logger.addHandler(ch) logger.addHandler(ch) # Log the startup app.logger.info('########################################################') app.logger.info('Starting %s v%s...', config.APP_NAME, config.APP_VERSION) app.logger.info('########################################################') app.logger.debug("Python syspath: %s", sys.path) from pgadmin.setup import create_app_data_directory, db_upgrade # Sanity checks (App data directory exists) create_app_data_directory(config) ########################################################################## # Setup i18n ########################################################################## # Initialise i18n babel = Babel(app) app.logger.debug('Available translations: %s' % babel.list_translations()) @babel.localeselector def get_locale(): """Get the language for the user.""" language = 'en' if config.SERVER_MODE is False: # Get the user language preference from the miscellaneous module misc_preference = Preferences.module('miscellaneous', False) if misc_preference: user_languages = misc_preference.preference('user_language') if user_languages: language = user_languages.get() or language else: # If language is available in get request then return the same # otherwise check the session or cookie data = request.form if 'language' in data: language = data['language'] or language setattr(session, 'PGADMIN_LANGUAGE', language) elif hasattr(session, 'PGADMIN_LANGUAGE'): language = getattr(session, 'PGADMIN_LANGUAGE', language) elif hasattr(request.cookies, 'PGADMIN_LANGUAGE'): language = getattr(request.cookies, 'PGADMIN_LANGUAGE', language) return language ########################################################################## # Setup authentication ########################################################################## app.config[ 'SQLALCHEMY_DATABASE_URI'] = u'sqlite:///{0}?timeout={1}'.format( config.SQLITE_PATH.replace(u'\\', u'/'), getattr(config, 'SQLITE_TIMEOUT', 500)) # Only enable password related functionality in server mode. if config.SERVER_MODE is True: # TODO: Figure out how to disable /logout and /login app.config['SECURITY_RECOVERABLE'] = True app.config['SECURITY_CHANGEABLE'] = True # Create database connection object and mailer db.init_app(app) ########################################################################## # Upgrade the schema (if required) ########################################################################## db_upgrade(app) Mail(app) import pgadmin.utils.paths as paths paths.init_app(app) # Setup Flask-Security user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(None, user_datastore) ########################################################################## # Setup security ########################################################################## with app.app_context(): config.CSRF_SESSION_KEY = Keys.query.filter_by( name='CSRF_SESSION_KEY').first().value config.SECRET_KEY = Keys.query.filter_by( name='SECRET_KEY').first().value config.SECURITY_PASSWORD_SALT = Keys.query.filter_by( name='SECURITY_PASSWORD_SALT').first().value # Update the app.config with proper security keyes for signing CSRF data, # signing cookies, and the SALT for hashing the passwords. app.config.update(dict(CSRF_SESSION_KEY=config.CSRF_SESSION_KEY)) app.config.update(dict(SECRET_KEY=config.SECRET_KEY)) app.config.update( dict(SECURITY_PASSWORD_SALT=config.SECURITY_PASSWORD_SALT)) security.init_app(app) app.session_interface = create_session_interface(app) ########################################################################## # Load all available server drivers ########################################################################## driver.init_app(app) ########################################################################## # Register language to the preferences after login ########################################################################## @user_logged_in.connect_via(app) def register_language(sender, user): # After logged in, set the language in the preferences if we get from # the login page data = request.form if 'language' in data: language = data['language'] # Set the user language preference misc_preference = Preferences.module('miscellaneous') user_languages = misc_preference.preference('user_language') if user_languages and language: language = user_languages.set(language) ########################################################################## # Register any local servers we can discover ########################################################################## @user_logged_in.connect_via(app) def on_user_logged_in(sender, user): # Keep hold of the user ID user_id = user.id # Get the first server group for the user servergroup_id = 1 servergroups = ServerGroup.query.filter_by( user_id=user_id).order_by("id") if servergroups.count() > 0: servergroup = servergroups.first() servergroup_id = servergroup.id '''Add a server to the config database''' def add_server(user_id, servergroup_id, name, superuser, port, discovery_id, comment): # Create a server object if needed, and store it. servers = Server.query.filter_by( user_id=user_id, discovery_id=svr_discovery_id).order_by("id") if servers.count() > 0: return svr = Server(user_id=user_id, servergroup_id=servergroup_id, name=name, host='localhost', port=port, maintenance_db='postgres', username=superuser, ssl_mode='prefer', comment=svr_comment, discovery_id=discovery_id) db.session.add(svr) db.session.commit() # Figure out what servers are present if winreg is not None: arch_keys = set() proc_arch = os.environ['PROCESSOR_ARCHITECTURE'].lower() try: proc_arch64 = os.environ['PROCESSOR_ARCHITEW6432'].lower() except: proc_arch64 = None if proc_arch == 'x86' and not proc_arch64: arch_keys.add(0) elif proc_arch == 'x86' or proc_arch == 'amd64': arch_keys.add(winreg.KEY_WOW64_32KEY) arch_keys.add(winreg.KEY_WOW64_64KEY) for arch_key in arch_keys: for server_type in ('PostgreSQL', 'EnterpriseDB'): try: root_key = winreg.OpenKey( winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\" + server_type + "\Services", 0, winreg.KEY_READ | arch_key) for i in xrange(0, winreg.QueryInfoKey(root_key)[0]): inst_id = winreg.EnumKey(root_key, i) inst_key = winreg.OpenKey(root_key, inst_id) svr_name = winreg.QueryValueEx( inst_key, 'Display Name')[0] svr_superuser = winreg.QueryValueEx( inst_key, 'Database Superuser')[0] svr_port = winreg.QueryValueEx(inst_key, 'Port')[0] svr_discovery_id = inst_id svr_comment = gettext( "Auto-detected %s installation with the data directory at %s" % (winreg.QueryValueEx(inst_key, 'Display Name')[0], winreg.QueryValueEx(inst_key, 'Data Directory')[0])) add_server(user_id, servergroup_id, svr_name, svr_superuser, svr_port, svr_discovery_id, svr_comment) inst_key.Close() except: pass else: # We use the postgres-winreg.ini file on non-Windows try: from configparser import ConfigParser except ImportError: from ConfigParser import ConfigParser # Python 2 registry = ConfigParser() try: registry.read('/etc/postgres-reg.ini') sections = registry.sections() # Loop the sections, and get the data from any that are PG or PPAS for section in sections: if section.startswith('PostgreSQL/') or section.startswith( 'EnterpriseDB/'): svr_name = registry.get(section, 'Description') svr_superuser = registry.get(section, 'Superuser') svr_port = registry.getint(section, 'Port') svr_discovery_id = section description = registry.get(section, 'Description') data_directory = registry.get(section, 'DataDirectory') if hasattr(str, 'decode'): description = description.decode('utf-8') data_directory = data_directory.decode('utf-8') svr_comment = gettext( u"Auto-detected %s installation with the data directory at %s" % (description, data_directory)) add_server(user_id, servergroup_id, svr_name, svr_superuser, svr_port, svr_discovery_id, svr_comment) except: pass ########################################################################## # Load plugin modules ########################################################################## for module in app.find_submodules('pgadmin'): app.logger.info('Registering blueprint module: %s' % module) app.register_blueprint(module) ########################################################################## # Handle the desktop login ########################################################################## @app.before_request def before_request(): """Login the default user if running in desktop mode""" # Check the auth key is valid, if it's set, and we're not in server # mode, and it's not a help file request. if not config.SERVER_MODE and app.PGADMIN_KEY != '': if ((not 'key' in request.args or request.args['key'] != app.PGADMIN_KEY) and request.cookies.get('PGADMIN_KEY') != app.PGADMIN_KEY and request.endpoint != 'help.static'): abort(401) if not config.SERVER_MODE: user = user_datastore.get_user(config.DESKTOP_USER) # Throw an error if we failed to find the desktop user, to give # the sysadmin a hint. We'll continue to try to login anyway as # that'll through a nice 500 error for us. if user is None: app.logger.error( 'The desktop user %s was not found in the configuration database.' % config.DESKTOP_USER) abort(401) login_user(user) @app.after_request def after_request(response): if 'key' in request.args: response.set_cookie('PGADMIN_KEY', value=request.args['key']) return response ########################################################################## # Minify output ########################################################################## # HTMLMIN doesn't work with Python 2.6. if not config.DEBUG and sys.version_info >= (2, 7): HTMLMIN(app) @app.context_processor def inject_blueprint(): """Inject a reference to the current blueprint, if any.""" return { 'current_app': current_app, 'current_blueprint': current_blueprint } ########################################################################## # All done! ########################################################################## app.logger.debug('URL map: %s' % app.url_map) return app
def create_app(config=None): """ config should be a python file """ from pathlib import Path from flask import (Flask, current_app, g, session, url_for, render_template) from flask_sqlalchemy import SQLAlchemy from flask_security import (Security, SQLAlchemyUserDatastore) from flask_wtf.csrf import CsrfProtect from flask_assets import (Environment, Bundle) from .app_setup import (init_db, setup_dirs) from .core import (db, load_blueprints, setup_logger) from .lib.template_filters import ( fmt_datetime, none_as_str, next_page_url, prev_page_url, get_page_url, get_images) from .models.user import (User, Role, user_datastore) from .Admin import (index, series, images, texts, contact) from .Public import (index, contact, texts) from .Security import user app = Flask(__name__.split('.')[0], instance_relative_config=True) app.config.from_object('config') app.config.from_pyfile('config.py') if config is not None: app.config.from_pyfile(config) setup_logger(app) app.logger.info('Started with config from: {}'.format(config)) else: setup_logger(app) app.logger.info('Started App') # Flask.sqlalchemy db.init_app(app) load_blueprints(app) # make sure db tables and required directories exist before_first_request_funcs = [setup_dirs(app), init_db(app)] #Security csrf = CsrfProtect() csrf.init_app(app) security = Security() security.init_app(app, user_datastore, register_blueprint=False) # Assets assets = Environment(app=app) assets.from_yaml('assets.yml') # template filters app.add_template_filter(fmt_datetime) app.add_template_filter(none_as_str) app.add_template_filter(next_page_url) app.add_template_filter(prev_page_url) app.add_template_filter(get_page_url) app.add_template_filter(get_images) return app
class InvenioAccounts(object): """Invenio-Accounts extension.""" def __init__(self, app=None, sessionstore=None): """Extension initialization. :param app: The Flask application. :param sessionstore: store for sessions. Passed to ``flask-kvsession``. Defaults to redis. """ self.security = Security() self.datastore = None if app: self.init_app(app, sessionstore=sessionstore) @staticmethod def monkey_patch_flask_security(): """Monkey-patch Flask-Security.""" if utils.get_hmac != get_hmac: utils.get_hmac = get_hmac if utils.encrypt_password != encrypt_password: utils.encrypt_password = encrypt_password changeable.encrypt_password = encrypt_password recoverable.encrypt_password = encrypt_password registerable.encrypt_password = encrypt_password def init_app(self, app, sessionstore=None, register_blueprint=True): """Flask application initialization. The following actions are executed: #. Initialize the configuration. #. Monkey-patch Flask-Security. #. Create the user datastore. #. Create the sessionstore. #. Initialize the extension, the forms to register users and confirms their emails, the CLI and, if ``ACCOUNTS_USE_CELERY`` is ``True``, register a celery task to send emails. :param app: The Flask application. :param sessionstore: store for sessions. Passed to ``flask-kvsession``. If ``None`` then Redis is configured. (Default: ``None``) :param register_blueprint: If ``True``, the application registers the blueprints. (Default: ``True``) """ self.init_config(app) # Monkey-patch Flask-Security InvenioAccounts.monkey_patch_flask_security() # Create user datastore if not self.datastore: self.datastore = SessionAwareSQLAlchemyUserDatastore( db, User, Role) # Create sessionstore if sessionstore is None: if app.testing and \ os.environ.get('CI', 'false') == 'false': from simplekv.memory import DictStore sessionstore = DictStore() else: import redis from simplekv.memory.redisstore import RedisStore sessionstore = RedisStore(redis.StrictRedis.from_url( app.config['ACCOUNTS_SESSION_REDIS_URL'])) user_logged_in.connect(login_listener, app) # Initialize extension. _register_blueprint = app.config.get('ACCOUNTS_REGISTER_BLUEPRINT') if _register_blueprint is not None: register_blueprint = _register_blueprint state = self.security.init_app(app, datastore=self.datastore, register_blueprint=register_blueprint) self.kvsession_extension = KVSessionExtension(sessionstore, app) app.extensions['security'].register_form = register_form_factory( app.extensions['security'].register_form, app) app.extensions['security'].confirm_register_form = \ confirm_register_form_factory( app.extensions['security'].confirm_register_form, app ) if app.config['ACCOUNTS_USE_CELERY']: from invenio_accounts.tasks import send_security_email @state.send_mail_task def delay_security_email(msg): send_security_email.delay(msg.__dict__) app.extensions['invenio-accounts'] = self def init_config(self, app): """Initialize configuration. :param app: The Flask application. """ try: pkg_resources.get_distribution('celery') app.config.setdefault( "ACCOUNTS_USE_CELERY", not (app.debug or app.testing)) except pkg_resources.DistributionNotFound: # pragma: no cover app.config.setdefault("ACCOUNTS_USE_CELERY", False) app.config.setdefault('ACCOUNTS', True) # Register Invenio legacy password hashing register_crypt_handler(InvenioAesEncryptedEmail) # Change Flask-Security defaults app.config.setdefault('SECURITY_CHANGEABLE', True) app.config.setdefault('SECURITY_CONFIRMABLE', True) app.config.setdefault('SECURITY_PASSWORD_HASH', 'pbkdf2_sha512') app.config.setdefault('SECURITY_PASSWORD_SCHEMES', ['pbkdf2_sha512', 'invenio_aes_encrypted_email']) app.config.setdefault('SECURITY_DEPRECATED_PASSWORD_SCHEMES', ['invenio_aes_encrypted_email']) app.config.setdefault('SECURITY_RECOVERABLE', True) app.config.setdefault('SECURITY_REGISTERABLE', True) app.config.setdefault('SECURITY_TRACKABLE', True) app.config.setdefault('SECURITY_LOGIN_WITHOUT_CONFIRMATION', True) app.config.setdefault('SECURITY_PASSWORD_SALT', app.config['SECRET_KEY']) # Change default templates app.config.setdefault("SECURITY_FORGOT_PASSWORD_TEMPLATE", "invenio_accounts/forgot_password.html") app.config.setdefault("SECURITY_LOGIN_USER_TEMPLATE", "invenio_accounts/login_user.html") app.config.setdefault("SECURITY_REGISTER_USER_TEMPLATE", "invenio_accounts/register_user.html") app.config.setdefault("SECURITY_RESET_PASSWORD_TEMPLATE", "invenio_accounts/reset_password.html") app.config.setdefault("SECURITY_CHANGE_PASSWORD_TEMPLATE", "invenio_accounts/change_password.html") app.config.setdefault("SECURITY_SEND_CONFIRMATION_TEMPLATE", "invenio_accounts/send_confirmation.html") app.config.setdefault("SECURITY_SEND_LOGIN_TEMPLATE", "invenio_accounts/send_login.html") app.config.setdefault("SECURITY_REGISTER_URL", "/signup/") app.config.setdefault("SECURITY_RESET_URL", "/lost-password/") app.config.setdefault("SECURITY_LOGIN_URL", "/login/") app.config.setdefault("SECURITY_LOGOUT_URL", "/logout/") app.config.setdefault("SECURITY_CHANGE_URL", "/accounts/settings/password/") for k in dir(config): if k.startswith('ACCOUNTS_'): app.config.setdefault(k, getattr(config, k))