From 695dba98f96eac3e07d2c37dcdf1b691a5c9c38d Mon Sep 17 00:00:00 2001 From: Sachi King Date: Thu, 2 Jun 2016 17:39:12 +1000 Subject: [PATCH] Use properties in config for pass-through compat vars Strings are immutable, so when one variable changes the other will not, as such there is variance on config load between variables that should be the the value of another variable. To solve that I've moved the config into a class and made those values read-only properties to the loaded values. Closes: #137 #138 --- realms/__init__.py | 2 +- realms/commands.py | 2 + realms/config/__init__.py | 338 ++++++++++++++-------------- realms/modules/auth/oauth/models.py | 2 + 4 files changed, 179 insertions(+), 165 deletions(-) diff --git a/realms/__init__.py b/realms/__init__.py index 8a9bda1..df06208 100644 --- a/realms/__init__.py +++ b/realms/__init__.py @@ -164,7 +164,7 @@ def error_handler(e): def create_app(config=None): app = Application(__name__) - app.config.from_object('realms.config') + app.config.from_object('realms.config.conf') app.url_map.converters['regex'] = RegexConverter app.url_map.strict_slashes = False diff --git a/realms/commands.py b/realms/commands.py index 5a140bd..acb50fb 100644 --- a/realms/commands.py +++ b/realms/commands.py @@ -10,6 +10,8 @@ import pip import time import subprocess +config = config.conf + # called to discover commands in modules app = create_app() diff --git a/realms/config/__init__.py b/realms/config/__init__.py index a11bab5..891f0ea 100644 --- a/realms/config/__init__.py +++ b/realms/config/__init__.py @@ -1,208 +1,218 @@ -import os import json +import os +import sys from urlparse import urlparse + from realms.lib.util import in_vagrant -def update(data): - conf = read() - conf.update(data) - return save(data) +class Config(object): + urlparse = urlparse -def read(): - conf = dict() + APP_PATH = os.path.abspath(os.path.dirname(__file__) + "/../..") + USER_HOME = os.path.abspath(os.path.expanduser("~")) - for k, v in os.environ.items(): - if k.startswith('REALMS_'): - conf[k[7:]] = v + # Best to change to /var/run + PIDFILE = "/tmp/realms-wiki.pid" - loc = get_path() + ENV = 'DEV' - if loc: - with open(loc) as f: - conf.update(json.load(f)) + HOST = "0.0.0.0" + PORT = 5000 + BASE_URL = 'http://localhost' + SITE_TITLE = "Realms" - for k in ['APP_PATH', 'USER_HOME']: - if k in conf: - del conf[k] + # https://pythonhosted.org/Flask-SQLAlchemy/config.html#connection-uri-format + DB_URI = 'sqlite:////tmp/wiki.db' + # DB_URI = 'mysql://scott:tiger@localhost/mydatabase' + # DB_URI = 'postgresql://scott:tiger@localhost/mydatabase' + # DB_URI = 'oracle://scott:tiger@127.0.0.1:1521/sidname' + # DB_URI = 'crate://' - return conf + # LDAP = { + # 'URI': '', + # + # # This BIND_DN/BIND_PASSWORD default to '', this is shown here for demonstrative purposes + # # The values '' perform an anonymous bind so we may use search/bind method + # 'BIND_DN': '', + # 'BIND_AUTH': '', + # + # # Adding the USER_SEARCH field tells the flask-ldap-login that we are using + # # the search/bind method + # 'USER_SEARCH': {'base': 'dc=example,dc=com', 'filter': 'uid=%(username)s'}, + # + # # Map ldap keys into application specific keys + # 'KEY_MAP': { + # 'name': 'cn', + # 'company': 'o', + # 'location': 'l', + # 'email': 'mail', + # } + # } + # OAUTH = { + # 'twitter': { + # 'key': '', + # 'secret': '' + # }, + # 'github': { + # 'key': '', + # 'secret': '' + # } + # } -def save(conf): - loc = get_path(check_write=True) - with open(loc, 'w') as f: - f.write(json.dumps(conf, sort_keys=True, indent=4, separators=(',', ': ')).strip() + '\n') - return loc + # Valid options: simple, redis, memcached + CACHE_TYPE = 'simple' + CACHE_REDIS_HOST = '127.0.0.1' + CACHE_REDIS_PORT = 6379 + CACHE_REDIS_DB = '0' -def get_path(check_write=False): - """Find config path - """ - for loc in os.curdir, os.path.expanduser("~"), "/etc/realms-wiki": - if not loc: - continue - path = os.path.join(loc, "realms-wiki.json") - if os.path.isfile(path): - # file exists - if not check_write: - # Don't care if I can write - return path + CACHE_MEMCACHED_SERVERS = ['127.0.0.1:11211'] - if os.access(path, os.W_OK): - # Has write access, ok! - return path - elif os.path.isdir(loc) and check_write: - # dir exists file doesn't - if os.access(loc, os.W_OK): - # can write file - return path - return None + # Valid options: simple, elasticsearch, woosh + SEARCH_TYPE = 'simple' + ELASTICSEARCH_URL = 'http://127.0.0.1:9200' + ELASTICSEARCH_FIELDS = ["name"] -APP_PATH = os.path.abspath(os.path.dirname(__file__) + "/../..") -USER_HOME = os.path.abspath(os.path.expanduser("~")) + WHOOSH_INDEX = '/tmp/whoosh' + WHOOSH_LANGUAGE = 'en' -# Best to change to /var/run -PIDFILE = "/tmp/realms-wiki.pid" + # Get ReCaptcha Keys for your domain here: + # https://www.google.com/recaptcha/admin#whyrecaptcha + RECAPTCHA_ENABLE = False + RECAPTCHA_USE_SSL = False + RECAPTCHA_PUBLIC_KEY = "6LfYbPkSAAAAAB4a2lG2Y_Yjik7MG9l4TDzyKUao" + RECAPTCHA_PRIVATE_KEY = "6LfYbPkSAAAAAG-KlkwjZ8JLWgwc9T0ytkN7lWRE" + RECAPTCHA_OPTIONS = {} -ENV = 'DEV' + SECRET_KEY = 'CHANGE_ME' -DEBUG = True -ASSETS_DEBUG = True -SQLALCHEMY_ECHO = False + # Path on file system where wiki data will reside + WIKI_PATH = '/tmp/wiki' -HOST = "0.0.0.0" -PORT = 5000 -BASE_URL = 'http://localhost' -SITE_TITLE = "Realms" + # Name of page that will act as home + WIKI_HOME = 'home' -# https://pythonhosted.org/Flask-SQLAlchemy/config.html#connection-uri-format -DB_URI = 'sqlite:////tmp/wiki.db' -# DB_URI = 'mysql://scott:tiger@localhost/mydatabase' -# DB_URI = 'postgresql://scott:tiger@localhost/mydatabase' -# DB_URI = 'oracle://scott:tiger@127.0.0.1:1521/sidname' -# DB_URI = 'crate://' + AUTH_LOCAL_ENABLE = True + ALLOW_ANON = True + REGISTRATION_ENABLED = True + PRIVATE_WIKI = False -# LDAP = { -# 'URI': '', -# -# # This BIND_DN/BIND_PASSWORD default to '', this is shown here for demonstrative purposes -# # The values '' perform an anonymous bind so we may use search/bind method -# 'BIND_DN': '', -# 'BIND_AUTH': '', -# -# # Adding the USER_SEARCH field tells the flask-ldap-login that we are using -# # the search/bind method -# 'USER_SEARCH': {'base': 'dc=example,dc=com', 'filter': 'uid=%(username)s'}, -# -# # Map ldap keys into application specific keys -# 'KEY_MAP': { -# 'name': 'cn', -# 'company': 'o', -# 'location': 'l', -# 'email': 'mail', -# } -# } + # None, firepad, and/or togetherjs + COLLABORATION = 'togetherjs' -# OAUTH = { -# 'twitter': { -# 'key': '', -# 'secret': '' -# }, -# 'github': { -# 'key': '', -# 'secret': '' -# } -# } + # Required for firepad + FIREBASE_HOSTNAME = None -CACHE_TYPE = 'simple' + # Page names that can't be modified + WIKI_LOCKED_PAGES = [] -# Redis -# CACHE_TYPE = 'redis' -CACHE_REDIS_HOST = '127.0.0.1' -CACHE_REDIS_PORT = 6379 -CACHE_REDIS_DB = '0' + ROOT_ENDPOINT = 'wiki.page' -# Memcached -# CACHE_TYPE = 'memcached' -CACHE_MEMCACHED_SERVERS = ['127.0.0.1:11211'] + # Used by Flask-Login + @property + def LOGIN_DISABLED(self): + return self.ALLOW_ANON -SEARCH_TYPE = 'simple' # simple is not good for large wikis + # Depreciated variable name + @property + def LOCKED(self): + return self.WIKI_LOCKED_PAGES[:] -# SEARCH_TYPE = 'elasticsearch' -ELASTICSEARCH_URL = 'http://127.0.0.1:9200' -ELASTICSEARCH_FIELDS = ["name"] + @property + def SQLALCHEMY_DATABASE_URI(self): + return self.DB_URI -# SEARCH_TYPE = 'whoosh' -WHOOSH_INDEX = '/tmp/whoosh' -WHOOSH_LANGUAGE = 'en' + @property + def _url(self): + return urlparse(self.BASE_URL) -# Get ReCaptcha Keys for your domain here: -# https://www.google.com/recaptcha/admin#whyrecaptcha -RECAPTCHA_ENABLE = False -RECAPTCHA_USE_SSL = False -RECAPTCHA_PUBLIC_KEY = "6LfYbPkSAAAAAB4a2lG2Y_Yjik7MG9l4TDzyKUao" -RECAPTCHA_PRIVATE_KEY = "6LfYbPkSAAAAAG-KlkwjZ8JLWgwc9T0ytkN7lWRE" -RECAPTCHA_OPTIONS = {} + @property + def RELATIVE_PATH(self): + return self._url.path -SECRET_KEY = 'CHANGE_ME' - -# Path on file system where wiki data will reside -WIKI_PATH = '/tmp/wiki' - -# Name of page that will act as home -WIKI_HOME = 'home' - -AUTH_LOCAL_ENABLE = True -ALLOW_ANON = True -REGISTRATION_ENABLED = True -PRIVATE_WIKI = False - -# None, firepad, and/or togetherjs -COLLABORATION = 'togetherjs' - -# Required for firepad -FIREBASE_HOSTNAME = None - -# Page names that can't be modified -WIKI_LOCKED_PAGES = [] - -ROOT_ENDPOINT = 'wiki.page' - -# Used by Flask-Login -LOGIN_DISABLED = ALLOW_ANON - -# Depreciated variable name -LOCKED = WIKI_LOCKED_PAGES[:] - -if BASE_URL.endswith('/'): - BASE_URL = BASE_URL[:-1] - -SQLALCHEMY_DATABASE_URI = DB_URI - -_url = urlparse(BASE_URL) -RELATIVE_PATH = _url.path - -if in_vagrant(): - # sendfile doesn't work well with Virtualbox shared folders USE_X_SENDFILE = False -if ENV != "DEV": DEBUG = False ASSETS_DEBUG = False SQLALCHEMY_ECHO = False -MODULES = ['wiki', 'search', 'auth'] + MODULES = ['wiki', 'search', 'auth'] -globals().update(read()) + def __init__(self): + for k, v in self.read().iteritems(): + setattr(self, k, v) + if hasattr(self, 'AUTH_LOCAL_ENABLE'): + self.MODULES.append('auth.local') + if hasattr(self, 'OAUTH'): + self.MODULES.append('auth.oauth') + if hasattr(self, 'LDAP'): + self.MODULES.append('auth.ldap') + if in_vagrant(): + self.USE_X_SENDFILE = False + if self.ENV == "DEV": + self.DEBUG = True + self.ASSETS_DEBUG = True + self.SQLALCHEMY_ECHO = True + self.USE_X_SENDFILE = False -if globals().get('AUTH_LOCAL_ENABLE'): - MODULES.append('auth.local') + def update(self, data): + conf = self.read() + conf.update(data) + return self.save(data) -if globals().get('OAUTH'): - MODULES.append('auth.oauth') + def read(self): + conf = dict() -if globals().get('LDAP'): - MODULES.append('auth.ldap') + for k, v in os.environ.items(): + if k.startswith('REALMS_'): + conf[k[7:]] = v + + loc = self.get_path() + + if loc: + with open(loc) as f: + conf.update(json.load(f)) + + if 'BASE_URL' in conf and conf['BASE_URL'].endswith('/'): + conf['BASE_URL'] = conf['BASE_URL'][:-1] + + for k in ['APP_PATH', 'USER_HOME']: + if k in conf: + del conf[k] + + return conf + + def save(self, conf): + loc = self.get_path(check_write=True) + with open(loc, 'w') as f: + f.write(json.dumps(conf, sort_keys=True, indent=4, separators=(',', ': ')).strip() + '\n') + return loc + + def get_path(self, check_write=False): + """Find config path + """ + for loc in os.curdir, os.path.expanduser("~"), "/etc/realms-wiki": + if not loc: + continue + path = os.path.join(loc, "realms-wiki.json") + if os.path.isfile(path): + # file exists + if not check_write: + # Don't care if I can write + return path + + if os.access(path, os.W_OK): + # Has write access, ok! + return path + elif os.path.isdir(loc) and check_write: + # dir exists file doesn't + if os.access(loc, os.W_OK): + # can write file + return path + return None + +conf = Config() diff --git a/realms/modules/auth/oauth/models.py b/realms/modules/auth/oauth/models.py index eec97ad..72f0470 100644 --- a/realms/modules/auth/oauth/models.py +++ b/realms/modules/auth/oauth/models.py @@ -5,6 +5,8 @@ from flask_oauthlib.client import OAuth from realms import config from ..models import BaseUser +config = config.conf + oauth = OAuth() users = {}