def create_app(config=None): app = Flask(__name__) if config is not None: app.config.from_object(config) db.init_app(app) app.engine = db.create_engine(app.config['SQLALCHEMY_DATABASE_URI']) Bower(app) from .features_bp.views import features_bp app.register_blueprint(features_bp, url_prefix='') @app.cli.command('initdb') def initdb_command(): """Initializes the database.""" db.create_all() print('Initialized the database.') @app.cli.command('secretkey') def secretkey_command(): """Print the secret key.""" print(app.config['SECRET_KEY']) return app
def test_absolute_root_path(self): """ Test using an absolute path to BOWER_COMPONENTS_ROOT Copies the existing bower_components folder to 'bower_components_alt' Sets the absolute path to the new directory as BOWER_COMPONENTS_ROOT Validates that url_for continues to work & tests calling get on the provided URL. """ alt_dir = os.path.abspath(os.path.join(os.path.curdir, 'tests', 'bower_components_alt')) shutil.move(os.path.abspath(os.path.join(os.path.curdir, 'tests', 'bower_components')), alt_dir) self.app.config['BOWER_COMPONENTS_ROOT'] = alt_dir Bower(self.app) with self.app.app_context(): self.assertEqual(bower_url_for('jquery', 'dist/jquery.js'), "http://unit.test/bower/jquery/dist/jquery.min.js?version=2.1.3") url = bower_url_for('jquery', 'dist/jquery.js') client = self.app.test_client() self.assertEqual(client.get(url).status_code, 200) # move files back to leave it clean shutil.move(alt_dir, os.path.abspath(os.path.join(os.path.curdir, 'tests', 'bower_components')))
def test_url_error_handler(self): """ Test the flask build_error_handler functionality """ Bower(self.app) with self.app.app_context(): self.assertEqual(url_for('bower.static', filename='jquery/dist/jquery.js'), "http://unit.test/bower/jquery/dist/jquery.min.js?version=2.1.3")
def test_component_not_found(self): """ Test when a component is not found """ Bower(self.app) with self.app.app_context(): with self.assertRaises(BuildError): url_for('bower.static', filename='not_found/not_found.js')
def test_file_not_found(self): """ Test when a component exists but the file is not found in the component """ Bower(self.app) with self.app.app_context(): with self.assertRaises(BuildError): url_for('bower.static', filename='jquery/not_found.js')
def test_bower_url_for(self): """ Test of the bower_url_for function """ Bower(self.app) with self.app.app_context(): self.assertEqual(bower_url_for('jquery', 'dist/jquery.js'), "http://unit.test/bower/jquery/dist/jquery.min.js?version=2.1.3")
def test_bower_subdomain(self): """ Test the flask blueprint subdomain configuration """ self.app.config['BOWER_SUBDOMAIN'] = 'static' Bower(self.app) with self.app.app_context(): self.assertEqual(bower_url_for('jquery', 'dist/jquery.js'), "http://static.unit.test/bower/jquery/dist/jquery.min.js?version=2.1.3")
def test_build_error_handler_chain(self): """ Append an additional build_error_handler to validate that adjacent handlers will be called as well """ Bower(self.app) self.app.url_build_error_handlers.append(additional_build_error_handler) with self.app.app_context(): self.assertTrue(url_for('bower.static', filename='not_found/not_found.js'))
def test_bower_url_for_no_revving(self): """ Test the BOWER_QUERYSTRING_REVVING parameter """ self.app.config['BOWER_QUERYSTRING_REVVING'] = False Bower(self.app) with self.app.app_context(): self.assertEqual(bower_url_for('jquery', 'dist/jquery.js'), "http://unit.test/bower/jquery/dist/jquery.min.js")
def create_app(spark_context): global recommendation_engine recommendation_engine = YelpRecommenderEngine( {'spark_context': spark_context}) app = Flask(__name__) app.register_blueprint(main) app.static_folder = 'static' Bootstrap(app) Bower(app) GoogleMaps(app, key='AIzaSyAT3qnHmIi6ujVBhyoFGtgwKIPQMPaTWA4') return app
def __init__(self): template_path = os.path.join(APP_DIR, "app/templates") static_path = os.path.join(APP_DIR, "app/static") flask_app = Flask(__name__, static_folder=static_path, template_folder=template_path) self.__flask_app = flask_app flask_app.secret_key = "\x81\xb6\xcc\xbdep\xff\\\xfbu\xec~R\xb8S\x12\xddm0\x0e\xdc\xdc\x07\xee" babel = Babel(flask_app) Bower(flask_app) flask_app.before_request(self.__before_request) flask_app.context_processor(self.__inject_locale_info) self.__login_manager = flask_login.LoginManager() self.__login_manager.init_app(flask_app) main_controller = app.controllers.main.MainController( self.__get_locale) flask_app.add_url_rule('/', view_func=main_controller.root) flask_app.add_url_rule('/<locale>', view_func=main_controller.index) flask_app.add_url_rule('/datasets/', view_func=main_controller.datasets) # self.add_url_rule('/datasets/', fget=main_controller.datasets) # flask_app.add_url_rule('/kernels/', view_func=main_controller.kernels) self.add_url_rule('/kernels/', fget=main_controller.kernels) ds_factory = app.domain.dataset.DatasetFactory() ds_mapper = app.domain.dataset.DatasetMapper(DATASETS_PATH, ds_factory) ds_controller = app.controllers.dataset.DatasetController( ds_mapper, DATASETS_PATH) self.add_url_rule('/datasets/list/', fget=ds_controller.list) flask_app.add_url_rule('/storage/<path:ds_path>/', view_func=ds_controller.upload, methods=['POST']) flask_app.config['DEBUG'] = DEBUG flask_app.config['BABEL_DEFAULT_LOCALE'] = settings.DEFAULT_LOCALE flask_app.config[ 'BOWER_COMPONENTS_ROOT'] = 'app/static/bower_components' flask_app.config['MAX_CONTENT_LENGTH'] = settings.MAX_CONTENT_LENGTH wsgi_container = WSGIContainer(flask_app) # tornado settings handlers = [ (r".*", FallbackHandler, dict(fallback=wsgi_container)), ] s = dict(xsrf_cookies=False, cookie_secret="11oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=", debug=DEBUG) tornado.web.Application.__init__(self, handlers, **s)
def __init__(self, app=None, options=None, db=None): self._app = app self._db = db self.options = options if self.options is not None and 'conf_file' in self.options and self.options['conf_file'] is not None: logging_fileConfig(self.options['conf_file']) self._listener = None self._listener_lock = None self._sleep = 0.25 self.menu_left = [] # Bower self.bower = Bower() # Caching self.cache = Cache()
def __init__(self, debug, core, host, port, app_root): self.core = core self.flask = Flask("PyPush.web", static_folder=os.path.join(const.PUSH_WEB_DIR, "static"), template_folder=os.path.join(const.PUSH_WEB_DIR, "templates"), ) self.bower = Bower(self.flask) self.restful = Api(self.flask) self._applyDefaultConfig() self.host = host self.port = port self.debug = debug self.flask.config.update( APPLICATION_ROOT=app_root, )
def create_app(config_name): app = Flask(__name__, static_folder='dashboard/dist') if not config_name in config: raise ValueError("Invalid FLASK_CONFIG, choose one of %s" % str.join(', ', config.keys())) app.config.from_object(config[config_name]) config[config_name].init_app(app) from .api_1_0 import api as api_1_0_blueprint app.register_blueprint(api_1_0_blueprint, url_prefix='/api/1.0') from .dashboard import dashboard app.register_blueprint(dashboard) from .docs import docs app.register_blueprint(docs, url_prefix='/docs') Bower(app) return app
def init(conf=None, verbose=0, logfile=None, gunicorn=True, unittest=False, debug=False): """Initialize the whole application. :param conf: Configuration file to use :type conf: str :param verbose: Set the verbosity level :type verbose: int :param logfile: Store the logs in the given file :type logfile: str :param gunicorn: Enable gunicorn engine instead of flask's default :type gunicorn: bool :param unittest: Are we running tests (used for test only) :type unittest: bool :param debug: Enable debug mode :type debug: bool :returns: A :class:`burpui.server.BUIServer` object """ from flask_login import LoginManager from flask_bower import Bower from .utils import basic_login_from_request, ReverseProxied from .server import BUIServer as BurpUI from .routes import view from .api import api, apibp logger = logging.getLogger('burp-ui') # The debug argument used to be a boolean so we keep supporting this format if isinstance(verbose, bool): if verbose: verbose = logging.DEBUG else: verbose = logging.CRITICAL else: levels = [ logging.CRITICAL, logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG ] if verbose >= len(levels): verbose = len(levels) - 1 if not verbose: verbose = 0 verbose = levels[verbose] if logfile: from logging.handlers import RotatingFileHandler handler = RotatingFileHandler( logfile, maxBytes=1024 * 1024 * 100, backupCount=5 ) else: from logging import StreamHandler handler = StreamHandler() if verbose > logging.DEBUG: LOG_FORMAT = ( '[%(asctime)s] %(levelname)s in ' '%(module)s.%(funcName)s: %(message)s' ) else: LOG_FORMAT = ( '-' * 80 + '\n' + '%(levelname)s in %(module)s.%(funcName)s ' + '[%(pathname)s:%(lineno)d]:\n' + '%(message)s\n' + '-' * 80 ) handler.setLevel(verbose) handler.setFormatter(Formatter(LOG_FORMAT)) logger.setLevel(verbose) logger.addHandler(handler) logger.debug( 'conf: {}\n'.format(conf) + 'verbose: {}\n'.format(logging.getLevelName(verbose)) + 'logfile: {}\n'.format(logfile) + 'gunicorn: {}\n'.format(gunicorn) + 'debug: {}\n'.format(debug) + 'unittest: {}'.format(unittest) ) if not unittest: from ._compat import patch_json patch_json() if gunicorn: from gevent import monkey monkey.patch_all() # We initialize the core app = BurpUI() app.enable_logger() app.gunicorn = gunicorn app.config['CFG'] = None # FIXME: strange behavior when bundling errors # app.config['BUNDLE_ERRORS'] = True app.config['REMEMBER_COOKIE_HTTPONLY'] = True app.jinja_env.globals.update( isinstance=isinstance, list=list, version_id='{}-{}'.format(__version__, __release__) ) if debug and not gunicorn: # pragma: no cover app.config['DEBUG'] = True and not unittest app.config['TESTING'] = True and not unittest # Still need to test conf file here because the init function can be called # by gunicorn directly app.config['CFG'] = lookup_config(conf) logger.info('Using configuration: {}'.format(app.config['CFG'])) app.setup(app.config['CFG']) # manage application secret key if app.secret_key and (app.secret_key.lower() == 'none' or (app.secret_key.lower() == 'random' and gunicorn)): logger.warning('Your setup is not secure! Please consider setting a' ' secret key in your configuration file') app.secret_key = 'Burp-UI' if not app.secret_key or app.secret_key.lower() == 'random': from base64 import b64encode app.secret_key = b64encode(os.urandom(256)) app.wsgi_app = ReverseProxied(app.wsgi_app, app) # Manage gunicorn special tricks & improvements if gunicorn: # pragma: no cover logger.info('Using gunicorn') from werkzeug.contrib.fixers import ProxyFix if app.storage and app.storage.lower() == 'redis': if app.redis: part = app.redis.split(':') host = part[0] try: port = int(part[1]) except: port = 6379 else: host = 'localhost' port = 6379 logger.debug('Using redis {}:{}'.format(host, port)) try: from redis import Redis from flask_session import Session red = Redis(host=host, port=port) app.config['SESSION_TYPE'] = 'redis' app.config['SESSION_REDIS'] = red app.config['SESSION_USE_SIGNER'] = app.secret_key is not None app.config['SESSION_PERMANENT'] = False ses = Session() ses.init_app(app) except Exception as e: logger.warning('Unable to initialize redis: {}'.format(str(e))) pass api.cache.init_app( app, config={ 'CACHE_TYPE': 'redis', 'CACHE_REDIS_HOST': host, 'CACHE_REDIS_PORT': port, 'CACHE_REDIS_DB': 1 } ) # clear cache at startup in case we removed or added servers with app.app_context(): api.cache.clear() else: api.cache.init_app(app) app.wsgi_app = ProxyFix(app.wsgi_app) else: api.cache.init_app(app) # We initialize the API api.init_bui(app) api.version = __version__ api.release = __release__ api.__url__ = __url__ api.__doc__ = __doc__ app.register_blueprint(apibp) # Then we load our routes view.init_bui(app) view.__url__ = __url__ view.__doc__ = __doc__ app.register_blueprint(view) # And the login_manager app.login_manager = LoginManager() app.login_manager.login_view = 'view.login' app.login_manager.login_message_category = 'info' app.login_manager.session_protection = 'strong' app.login_manager.init_app(app) app.config.setdefault( 'BOWER_COMPONENTS_ROOT', os.path.join('static', 'vendor') ) app.config.setdefault('BOWER_REPLACE_URL_FOR', True) bower = Bower() bower.init_app(app) @app.before_request def setup_request(): if app.scookie: from flask import request criteria = [ request.is_secure, request.headers.get('X-Forwarded-Proto', 'http') == 'https' ] app.config['SESSION_COOKIE_SECURE'] = \ app.config['REMEMBER_COOKIE_SECURE'] = any(criteria) @app.login_manager.user_loader def load_user(userid): """User loader callback""" if app.auth != 'none': return app.uhandler.user(userid) return None @app.login_manager.request_loader def load_user_from_request(request): """User loader from request callback""" if app.auth != 'none': return basic_login_from_request(request, app) return app
arg_parser.add_argument('--regen_tlmdb', action="store_true", help='Regenerates the telemetry database') #arg_parser.add_argument('--uart', type=str,help='UART device to pull data from') #arg_parser.add_argument('-h', '--help', action="store_true", help='Disables output telemetry') arg_parser.add_help = False args = arg_parser.parse_known_args()[0] app = Flask(__name__) app._static_folder = os.path.dirname(sys.argv[0]) + './openmct-tutorial' app._static_folder = os.path.abspath(app._static_folder) app.config.update(user_dir=args.ProjectDir) #app.config.from_object('config') app.config['SECRET_KEY'] = 'secret, but not that secret!' cache = Cache(app, config={'CACHE_TYPE': 'simple'}) Bower(app) CORS(app) api = Api(app) socketio = SocketIO(app,logger=args.verbose,engineio_logger=args.verbose) #async_mode=async_mode, @app.route('/<path:path>') def static_file(path): print path, "Static request" , app._static_folder return app.send_static_file(path) @app.route('/') def index(): print "Static request" , app._static_folder + '/index.html' return app.send_static_file('index.html') @app.route('/db_telemetry')
response = urllib.request.urlopen( 'http://172.17.0.1:3000/api/active').read() socketio.emit('message', json.loads(response)) @socketio.on('connect') def connect(): global thread if thread is None: thread = socketio.start_background_task(target=background_thread) @app.route('/') def index(): return render_template('home.html') @app.route('/links') def links(): return render_template('links.html') @app.route('/dash') def dash(): return render_template('dashboard.html') if __name__ == '__main__': Bower(app=app) socketio.run(app, port=5000, debug=False)
#!/usr/bin/env python3 """ Inventory system for FRC Team 4099. """ from flask import Flask, render_template, request, Response, session from flask_session import Session from flask_bower import Bower from json import load, dump, dumps, decoder from jellyfish import jaro_winkler app = Flask(__name__) bower = Bower(app) sess = Session(app) ITEM_DATA = "items.json" LOG_FILE = "log.log" MAX_ITEMS = 10000 def log_to_file(str_to_log): with open(LOG_FILE, "a") as log: log.write(str_to_log) def update_item_file(): global data with open(ITEM_DATA, "w") as file: dump(data, file, indent=4)
def create_app(conf=None, verbose=0, logfile=None, **kwargs): """Initialize the whole application. :param conf: Configuration file to use :type conf: str :param verbose: Set the verbosity level :type verbose: int :param logfile: Store the logs in the given file :type logfile: str :param kwargs: Extra options: - gunicorn (bool): Enable gunicorn engine instead of flask's default. Default is True. - unittest (bool): Are we running tests (used for test only). Default is False. - debug (bool): Enable debug mode. Default is False. - cli (bool): Are we running the CLI. Default is False. - reverse_proxy (bool): Are we behind a reverse-proxy. Default is True if gunicorn is True - websocket_server (bool): Are we running the websocket server. Default is False :type kwargs: dict :returns: A :class:`burpui.engines.server.BUIServer` object """ from flask import g, request, session from flask_login import LoginManager from flask_bower import Bower from flask_babel import gettext from .utils import ReverseProxied, lookup_file, is_uuid from .tools.logging import logger from .security import basic_login_from_request from .engines.server import BUIServer as BurpUI from .sessions import session_manager from .filter import mask from .ext.cache import cache from .ext.i18n import babel, get_locale from .misc.auth.handler import BUIanon gunicorn = kwargs.get('gunicorn', True) unittest = kwargs.get('unittest', False) debug = kwargs.get('debug', False) cli = kwargs.get('cli', False) reverse_proxy = kwargs.get('reverse_proxy', gunicorn) celery_worker = kwargs.get('celery_worker', False) websocket_server = kwargs.get('websocket_server', False) # We initialize the core app = BurpUI() app.config['CFG'] = None app.config['LOG_FILE'] = logfile app.config['LOG_LEVEL'] = verbose logger.init_app(app) if verbose: app.enable_logger() app.gunicorn = gunicorn logger.debug('conf: {}\n'.format(conf) + 'verbose: {}\n'.format(logging.getLevelName(verbose)) + 'logfile: {}\n'.format(logfile) + 'gunicorn: {}\n'.format(gunicorn) + 'debug: {}\n'.format(debug) + 'unittest: {}\n'.format(unittest) + 'cli: {}\n'.format(cli) + 'reverse_proxy: {}\n'.format(reverse_proxy) + 'celery_worker: {}\n'.format(celery_worker) + 'websocket_server: {}'.format(websocket_server)) # Some config app.config['BUI_CLI'] = cli # FIXME: strange behavior when bundling errors # app.config['BUNDLE_ERRORS'] = True app.config['REMEMBER_COOKIE_HTTPONLY'] = True if debug and not gunicorn: # pragma: no cover app.config['DEBUG'] = True and not unittest app.config['TESTING'] = True and not unittest # Still need to test conf file here because the init function can be called # by gunicorn directly if conf: app.config['CFG'] = lookup_file(conf, guess=False) else: app.config['CFG'] = lookup_file() logger.info('Using configuration: {}'.format(app.config['CFG'])) app.setup(app.config['CFG'], unittest, cli) if cli and not websocket_server and 'shell' not in sys.argv: return app if debug: app.config.setdefault('TEMPLATES_AUTO_RELOAD', True) app.config['TEMPLATES_AUTO_RELOAD'] = True app.config['DEBUG'] = True if app.demo: try: import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration sentry_sdk.init(dsn=app.config['BUI_DSN'], integrations=[FlaskIntegration()]) except ImportError: pass # manage application secret key if app.secret_key and \ (app.secret_key.lower() == 'none' or (app.secret_key.lower() == 'random' and gunicorn)): # pragma: no cover logger.critical('Your setup is not secure! Please consider setting a' ' secret key in your configuration file') app.secret_key = 'Burp-UI' if not app.secret_key or app.secret_key.lower() == 'random': from base64 import b64encode app.secret_key = b64encode(os.urandom(256)) app.wsgi_app = ReverseProxied(app.wsgi_app, app) # Manage reverse_proxy special tricks & improvements if reverse_proxy: # pragma: no cover from werkzeug.middleware.proxy_fix import ProxyFix kwargs = {} if app.config['NUM_PROXIES'] > 0: kwargs = app.config['PROXY_FIX_ARGS'].format( num_proxies=app.config['NUM_PROXIES']) kwargs = json.loads(kwargs) logger.debug(f"Using {kwargs} as ProxyFix parameters") app.wsgi_app = ProxyFix(app.wsgi_app, **kwargs) if app.storage and app.storage.lower() == 'redis': try: # Session setup if not app.session_db or \ str(app.session_db).lower() not in ['none', 'false']: from redis import Redis from .ext.session import sess host, port, pwd = get_redis_server(app) db = 0 if app.session_db and \ str(app.session_db).lower() not \ in ['redis', 'default', 'true']: try: # pragma: no cover (_, _, pwd, host, port, db) = \ parse_db_setting(app.session_db) except ValueError as exp: logger.warning(str(exp)) try: db = int(db) except ValueError: db = 0 logger.debug( 'SESSION: Using redis://guest:****@{}:{}/{}'.format( host, port, db)) red = Redis(host=host, port=port, db=db, password=pwd) app.config['WITH_SRV_SESSION'] = True app.config['SESSION_TYPE'] = 'redis' app.config['SESSION_REDIS'] = red app.config['SESSION_USE_SIGNER'] = app.secret_key is not None app.config['SESSION_PERMANENT'] = False sess.init_app(app) session_manager.backend = red except Exception as exp: # pragma: no cover logger.warning('Unable to initialize session: {}'.format(str(exp))) app.config['WITH_SRV_SESSION'] = False try: # Cache setup if not app.cache_db or \ str(app.cache_db).lower() not in ['none', 'false']: host, port, pwd = get_redis_server(app) db = 1 if app.cache_db and \ str(app.cache_db).lower() not \ in ['redis', 'default', 'true']: try: # pragma: no cover (_, _, pwd, host, port, db) = \ parse_db_setting(app.cache_db) except ValueError as exp: logger.warning(str(exp)) try: db = int(db) except ValueError: db = 1 logger.debug('CACHE: Using redis://guest:****@{}:{}/{}'.format( host, port, db)) cache.init_app(app, config={ 'CACHE_TYPE': 'redis', 'CACHE_REDIS_HOST': host, 'CACHE_REDIS_PORT': port, 'CACHE_REDIS_PASSWORD': pwd, 'CACHE_REDIS_DB': db }) # clear cache at startup in case we removed or added servers with app.app_context(): cache.clear() else: # pragma: no cover cache.init_app(app) except Exception as exp: # pragma: no cover logger.warning('Unable to initialize cache: {}'.format(str(exp))) cache.init_app(app) try: # Limiter setup if app.limiter and str(app.limiter).lower() not \ in ['none', 'false']: # pragma: no cover from .ext.limit import limiter app.config['RATELIMIT_HEADERS_ENABLED'] = True if app.limiter and str(app.limiter).lower() not \ in ['default', 'redis', 'true']: app.config['RATELIMIT_STORAGE_URL'] = app.limiter else: db = 3 host, port, pwd = get_redis_server(app) if pwd: conn = 'redis://*****:*****@{}:{}/{}'.format( pwd, host, port, db) else: conn = 'redis://{}:{}/{}'.format(host, port, db) app.config['RATELIMIT_STORAGE_URL'] = conn (_, _, pwd, host, port, db) = parse_db_setting(app.config['RATELIMIT_STORAGE_URL']) logger.debug( 'LIMITER: Using redis://guest:****@{}:{}/{}'.format( host, port, db)) limiter.init_app(app) app.config['WITH_LIMIT'] = True except ImportError: # pragma: no cover logger.warning('Unable to load limiter. Did you run \'pip install ' 'flask-limiter\'?') except Exception as exp: # pragma: no cover logger.warning('Unable to initialize limiter: {}'.format(str(exp))) else: cache.init_app(app) # Initialize i18n babel.init_app(app) # Create SQLAlchemy if enabled create_db(app, cli, unittest, celery_worker=celery_worker) if not celery_worker: from .api import api, apibp from .routes import view, mypad app.jinja_env.globals.update( isinstance=isinstance, list=list, mypad=mypad, version_id='{}-{}'.format(__version__, __release__), ) # We initialize the API api.load_all() app.register_blueprint(apibp) # Then we load our routes app.register_blueprint(view) # Initialize Bower ext app.config.setdefault('BOWER_COMPONENTS_ROOT', os.path.join('static', 'vendor')) app.config.setdefault('BOWER_REPLACE_URL_FOR', True) bower = Bower() bower.init_app(app) # Order of the initialization matters! # The websocket must be configured prior to the celery worker for instance # Initialize Session Manager session_manager.init_app(app) # Initialize filter mask.init_app(app) # And the login_manager app.login_manager = LoginManager() app.login_manager.anonymous_user = BUIanon app.login_manager.login_view = 'view.login' app.login_manager.login_message_category = 'info' app.login_manager.session_protection = 'strong' # This is just to have the strings in the .po files app.login_manager.login_message = gettext( 'Please log in to access this page.') app.login_manager.needs_refresh_message = gettext( 'Please reauthenticate to access this page.') # This will be called at runtime and will then translate the strings app.login_manager.localize_callback = gettext app.login_manager.init_app(app) # Create WebSocket server if create_websocket(app, websocket_server, celery_worker, cli): return app # Create celery app if enabled create_celery(app, warn=False) def _check_session(user, request, api=False): """Check if the session is in the db""" if user and not session_manager.session_in_db(): # pragma: no cover login = getattr(user, 'name', None) if login and not is_uuid(login): remember = session.get('persistent', False) if not remember: from flask_login import decode_cookie remember_cookie = request.cookies.get( app.config.get('REMEMBER_COOKIE_NAME'), False) # check if the remember_cookie is legit if remember_cookie and decode_cookie(remember_cookie): remember = True session_manager.store_session( login, request.remote_addr, request.headers.get('User-Agent'), remember, api) elif login: app.uhandler.remove(login) @app.before_request def setup_request(): g.version = '{}-{}'.format(__version__, __release__) g.locale = get_locale() g.now = round(time.time()) g.date_format = session.get('dateFormat', 'llll') g.timezone = session.get('timezone') # make sure to store secure cookie if required if app.config['BUI_SCOOKIE']: criteria = (request.is_secure, request.headers.get('X-Forwarded-Proto', 'http') == 'https') app.config['SESSION_COOKIE_SECURE'] = \ app.config['REMEMBER_COOKIE_SECURE'] = any(criteria) if '_extra' in request.args: session['_extra'] = request.args.get('_extra') g._extra = session.get('_extra', '') @app.login_manager.user_loader def load_user(userid): """User loader callback""" if app.auth != 'none': user = app.uhandler.user(userid) if not user: return None if 'X-Language' in request.headers: language = request.headers.get('X-Language') user.language = language session['language'] = language if '_id' not in session: from flask_login import login_user # if _id is not in session, it means we loaded the user from # cache/db using the remember cookie so we need to login it login_user(user, remember=user.is_authenticated, fresh=False) _check_session(user, request) return user return None @app.login_manager.request_loader def load_user_from_request(request): """User loader from request callback""" if app.auth != 'none': user = basic_login_from_request(request, app) _check_session(user, request, True) return user @app.after_request def after_request(response): if getattr(g, 'basic_session', False): if session_manager.invalidate_current_session(): session_manager.delete_session() return response return app
def init(conf=None, verbose=0, logfile=None, gunicorn=True, unittest=False, debug=False): """Initialize the whole application. :param conf: Configuration file to use :type conf: str :param verbose: Set the verbosity level :type verbose: int :param logfile: Store the logs in the given file :type logfile: str :param gunicorn: Enable gunicorn engine instead of flask's default :type gunicorn: bool :param unittest: Are we running tests (used for test only) :type unittest: bool :param debug: Enable debug mode :type debug: bool :returns: A :class:`burpui.server.BUIServer` object """ from flask_login import LoginManager from flask_bower import Bower from .utils import basic_login_from_request from .server import BUIServer as BurpUI from .routes import view from .api import api, apibp logger = logging.getLogger('burp-ui') # The debug argument used to be a boolean so we keep supporting this format if isinstance(verbose, bool): if verbose: verbose = logging.DEBUG else: verbose = logging.CRITICAL else: levels = [ logging.CRITICAL, logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG ] if verbose >= len(levels): verbose = len(levels) - 1 if not verbose: verbose = 0 verbose = levels[verbose] if logfile: from logging.handlers import RotatingFileHandler handler = RotatingFileHandler(logfile, maxBytes=1024 * 1024 * 100, backupCount=20) else: from logging import StreamHandler handler = StreamHandler() if verbose > logging.DEBUG: LOG_FORMAT = ('[%(asctime)s] %(levelname)s in ' '%(module)s.%(funcName)s: %(message)s') else: LOG_FORMAT = ('-' * 80 + '\n' + '%(levelname)s in %(module)s.%(funcName)s ' + '[%(pathname)s:%(lineno)d]:\n' + '%(message)s\n' + '-' * 80) handler.setLevel(verbose) handler.setFormatter(Formatter(LOG_FORMAT)) logger.setLevel(verbose) logger.addHandler(handler) logger.debug('conf: {}\n'.format(conf) + 'verbose: {}\n'.format(logging.getLevelName(verbose)) + 'logfile: {}\n'.format(logfile) + 'gunicorn: {}\n'.format(gunicorn) + 'debug: {}\n'.format(debug) + 'unittest: {}'.format(unittest)) if not unittest: from ._compat import patch_json patch_json() if gunicorn: from gevent import monkey monkey.patch_all() # We initialize the core app = BurpUI() app.enable_logger() app.gunicorn = gunicorn app.config['CFG'] = None app.secret_key = ( 'VpgOXNXAgcO81xFPyWj07ppN6kExNZeCDRShseNzFKV7ZCgmW2/eLn6x' 'Slt7pYAVBj12zx2Vv9Kw3Q3jd1266A==') app.jinja_env.globals.update(isinstance=isinstance, list=list, version_id='{}-{}'.format( __version__, __release__)) if debug and not gunicorn: # pragma: no cover app.config['DEBUG'] = True and not unittest app.config['TESTING'] = True and not unittest # Still need to test conf file here because the init function can be called # by gunicorn directly app.config['CFG'] = lookup_config(conf) logger.info('Using configuration: {}'.format(app.config['CFG'])) app.setup(app.config['CFG']) if gunicorn: # pragma: no cover logger.info('Using gunicorn') from werkzeug.contrib.fixers import ProxyFix if app.storage and app.storage.lower() == 'redis': if app.redis: part = app.redis.split(':') host = part[0] try: port = int(part[1]) except: port = 6379 else: host = 'localhost' port = 6379 logger.debug('Using redis {}:{}'.format(host, port)) try: from redis import Redis from flask_session import Session red = Redis(host=host, port=port) app.config['SESSION_TYPE'] = 'redis' app.config['SESSION_REDIS'] = red app.config['SESSION_COOKIE_SECURE'] = app.scookie ses = Session() ses.init_app(app) except Exception as e: logger.warning('Unable to initialize redis: {}'.format(str(e))) pass api.cache.init_app(app, config={ 'CACHE_TYPE': 'redis', 'CACHE_REDIS_HOST': host, 'CACHE_REDIS_PORT': port, 'CACHE_REDIS_DB': 1 }) # clear cache at startup in case we removed or added servers with app.app_context(): api.cache.clear() else: api.cache.init_app(app) app.wsgi_app = ProxyFix(app.wsgi_app) else: api.cache.init_app(app) # Then we load our routes view.init_bui(app) view.__url__ = __url__ view.__doc__ = __doc__ app.register_blueprint(view) # We initialize the API api.init_bui(app) api.version = __version__ api.release = __release__ api.__url__ = __url__ api.__doc__ = __doc__ app.register_blueprint(apibp) # And the login_manager app.login_manager = LoginManager() app.login_manager.login_view = 'view.login' app.login_manager.login_message_category = 'info' app.login_manager.session_protection = 'strong' app.login_manager.init_app(app) app.config.setdefault('BOWER_COMPONENTS_ROOT', os.path.join('static', 'vendor')) app.config.setdefault('BOWER_REPLACE_URL_FOR', True) bower = Bower() bower.init_app(app) @app.login_manager.user_loader def load_user(userid): """User loader callback""" if app.auth != 'none': return app.uhandler.user(userid) return None # pragma: no cover @app.login_manager.request_loader def load_user_from_request(request): """User loader from request callback""" if app.auth != 'none': return basic_login_from_request(request, app) return app
def create_app(conf=None, verbose=0, logfile=None, **kwargs): """Initialize the whole application. :param conf: Configuration file to use :type conf: str :param verbose: Set the verbosity level :type verbose: int :param logfile: Store the logs in the given file :type logfile: str :param kwargs: Extra options: - gunicorn (bool): Enable gunicorn engine instead of flask's default. Default is True. - unittest (bool): Are we running tests (used for test only). Default is False. - debug (bool): Enable debug mode. Default is False. - cli (bool): Are we running the CLI. Default is False. - reverse_proxy (bool): Are we behind a reverse-proxy. Default is True if gunicorn is True - websocket_server (bool): Are we running the websocket server. Default is False :type kwargs: dict :returns: A :class:`burpui.engines.server.BUIServer` object """ from flask import g, request, session, render_template from flask_login import LoginManager from flask_bower import Bower from flask_babel import gettext from .utils import ReverseProxied, lookup_file, is_uuid from .tools.logging import logger from .security import basic_login_from_request from .engines.server import BUIServer as BurpUI from .sessions import session_manager from .filter import mask from .ext.cache import cache from .ext.i18n import babel, get_locale from .misc.auth.handler import BUIanon gunicorn = kwargs.get('gunicorn', True) unittest = kwargs.get('unittest', False) debug = kwargs.get('debug', False) cli = kwargs.get('cli', False) reverse_proxy = kwargs.get('reverse_proxy', gunicorn) celery_worker = kwargs.get('celery_worker', False) websocket_server = kwargs.get('websocket_server', False) # We initialize the core app = BurpUI() app.config['CFG'] = None app.config['LOG_FILE'] = logfile app.config['LOG_LEVEL'] = verbose logger.init_app(app) if verbose: app.enable_logger() app.gunicorn = gunicorn logger.debug( 'conf: {}\n'.format(conf) + 'verbose: {}\n'.format(logging.getLevelName(verbose)) + 'logfile: {}\n'.format(logfile) + 'gunicorn: {}\n'.format(gunicorn) + 'debug: {}\n'.format(debug) + 'unittest: {}\n'.format(unittest) + 'cli: {}\n'.format(cli) + 'reverse_proxy: {}\n'.format(reverse_proxy) + 'celery_worker: {}\n'.format(celery_worker) + 'websocket_server: {}'.format(websocket_server) ) # Some config app.config['BUI_CLI'] = cli # FIXME: strange behavior when bundling errors # app.config['BUNDLE_ERRORS'] = True app.config['REMEMBER_COOKIE_HTTPONLY'] = True if debug and not gunicorn: # pragma: no cover app.config['DEBUG'] = True and not unittest app.config['TESTING'] = True and not unittest # Still need to test conf file here because the init function can be called # by gunicorn directly if conf: app.config['CFG'] = lookup_file(conf, guess=False) else: app.config['CFG'] = lookup_file() logger.info('Using configuration: {}'.format(app.config['CFG'])) app.setup(app.config['CFG'], unittest, cli) if cli and not websocket_server: return app if debug: app.config.setdefault('TEMPLATES_AUTO_RELOAD', True) app.config['TEMPLATES_AUTO_RELOAD'] = True app.config['DEBUG'] = True SENTRY_AVAILABLE = False if app.demo: try: from .ext.sentry import sentry sentry.init_app(app, dsn=app.config['BUI_DSN']) SENTRY_AVAILABLE = True except ImportError: pass # manage application secret key if app.secret_key and \ (app.secret_key.lower() == 'none' or (app.secret_key.lower() == 'random' and gunicorn)): # pragma: no cover logger.critical('Your setup is not secure! Please consider setting a' ' secret key in your configuration file') app.secret_key = 'Burp-UI' if not app.secret_key or app.secret_key.lower() == 'random': from base64 import b64encode app.secret_key = b64encode(os.urandom(256)) app.wsgi_app = ReverseProxied(app.wsgi_app, app) # Manage reverse_proxy special tricks & improvements if reverse_proxy: # pragma: no cover from werkzeug.middleware.proxy_fix import ProxyFix kwargs = {} if app.config['NUM_PROXIES'] > 0: kwargs = app.config['PROXY_FIX_ARGS'].format(num_proxies=app.config['NUM_PROXIES']) kwargs = json.loads(kwargs) logger.debug(f"Using {kwargs} as ProxyFix parameters") app = ProxyFix(app, **kwargs) if app.storage and app.storage.lower() == 'redis': try: # Session setup if not app.session_db or \ str(app.session_db).lower() not in ['none', 'false']: from redis import Redis from .ext.session import sess host, port, pwd = get_redis_server(app) db = 0 if app.session_db and \ str(app.session_db).lower() not \ in ['redis', 'default', 'true']: try: # pragma: no cover (_, _, pwd, host, port, db) = \ parse_db_setting(app.session_db) except ValueError as exp: logger.warning(str(exp)) try: db = int(db) except ValueError: db = 0 logger.debug( 'SESSION: Using redis://guest:****@{}:{}/{}'.format( host, port, db) ) red = Redis(host=host, port=port, db=db, password=pwd) app.config['WITH_SRV_SESSION'] = True app.config['SESSION_TYPE'] = 'redis' app.config['SESSION_REDIS'] = red app.config['SESSION_USE_SIGNER'] = app.secret_key is not None app.config['SESSION_PERMANENT'] = False sess.init_app(app) session_manager.backend = red except Exception as exp: # pragma: no cover logger.warning('Unable to initialize session: {}'.format(str(exp))) app.config['WITH_SRV_SESSION'] = False try: # Cache setup if not app.cache_db or \ str(app.cache_db).lower() not in ['none', 'false']: host, port, pwd = get_redis_server(app) db = 1 if app.cache_db and \ str(app.cache_db).lower() not \ in ['redis', 'default', 'true']: try: # pragma: no cover (_, _, pwd, host, port, db) = \ parse_db_setting(app.cache_db) except ValueError as exp: logger.warning(str(exp)) try: db = int(db) except ValueError: db = 1 logger.debug('CACHE: Using redis://guest:****@{}:{}/{}'.format( host, port, db) ) cache.init_app( app, config={ 'CACHE_TYPE': 'redis', 'CACHE_REDIS_HOST': host, 'CACHE_REDIS_PORT': port, 'CACHE_REDIS_PASSWORD': pwd, 'CACHE_REDIS_DB': db } ) # clear cache at startup in case we removed or added servers with app.app_context(): cache.clear() else: # pragma: no cover cache.init_app(app) except Exception as exp: # pragma: no cover logger.warning('Unable to initialize cache: {}'.format(str(exp))) cache.init_app(app) try: # Limiter setup if app.limiter and str(app.limiter).lower() not \ in ['none', 'false']: # pragma: no cover from .ext.limit import limiter app.config['RATELIMIT_HEADERS_ENABLED'] = True if app.limiter and str(app.limiter).lower() not \ in ['default', 'redis', 'true']: app.config['RATELIMIT_STORAGE_URL'] = app.limiter else: db = 3 host, port, pwd = get_redis_server(app) if pwd: conn = 'redis://*****:*****@{}:{}/{}'.format( pwd, host, port, db ) else: conn = 'redis://{}:{}/{}'.format(host, port, db) app.config['RATELIMIT_STORAGE_URL'] = conn (_, _, pwd, host, port, db) = parse_db_setting( app.config['RATELIMIT_STORAGE_URL'] ) logger.debug( 'LIMITER: Using redis://guest:****@{}:{}/{}'.format( host, port, db ) ) limiter.init_app(app) app.config['WITH_LIMIT'] = True except ImportError: # pragma: no cover logger.warning('Unable to load limiter. Did you run \'pip install ' 'flask-limiter\'?') except Exception as exp: # pragma: no cover logger.warning('Unable to initialize limiter: {}'.format(str(exp))) else: cache.init_app(app) # Initialize i18n babel.init_app(app) # Create SQLAlchemy if enabled create_db(app, cli, unittest, celery_worker=celery_worker) if not celery_worker: from .api import api, apibp from .routes import view, mypad app.jinja_env.globals.update( isinstance=isinstance, list=list, mypad=mypad, version_id='{}-{}'.format(__version__, __release__), ) # We initialize the API api.load_all() app.register_blueprint(apibp) # Then we load our routes app.register_blueprint(view) # Initialize Bower ext app.config.setdefault( 'BOWER_COMPONENTS_ROOT', os.path.join('static', 'vendor') ) app.config.setdefault('BOWER_REPLACE_URL_FOR', True) bower = Bower() bower.init_app(app) # Order of the initialization matters! # The websocket must be configured prior to the celery worker for instance # Initialize Session Manager session_manager.init_app(app) # Initialize filter mask.init_app(app) # And the login_manager app.login_manager = LoginManager() app.login_manager.anonymous_user = BUIanon app.login_manager.login_view = 'view.login' app.login_manager.login_message_category = 'info' app.login_manager.session_protection = 'strong' # This is just to have the strings in the .po files app.login_manager.login_message = gettext( 'Please log in to access this page.' ) app.login_manager.needs_refresh_message = gettext( 'Please reauthenticate to access this page.' ) # This will be called at runtime and will then translate the strings app.login_manager.localize_callback = gettext app.login_manager.init_app(app) # Create WebSocket server if create_websocket(app, websocket_server, celery_worker, gunicorn, cli): return app # Create celery app if enabled create_celery(app, warn=False) def _check_session(user, request, api=False): """Check if the session is in the db""" if user and not session_manager.session_in_db(): # pragma: no cover login = getattr(user, 'name', None) if login and not is_uuid(login): remember = session.get('persistent', False) if not remember: from flask_login import decode_cookie remember_cookie = request.cookies.get( app.config.get('REMEMBER_COOKIE_NAME'), False ) # check if the remember_cookie is legit if remember_cookie and decode_cookie(remember_cookie): remember = True session_manager.store_session( login, request.remote_addr, request.headers.get('User-Agent'), remember, api ) elif login: app.uhandler.remove(login) @app.before_request def setup_request(): g.version = '{}-{}'.format(__version__, __release__) g.locale = get_locale() g.now = round(time.time()) g.date_format = session.get('dateFormat', 'llll') # make sure to store secure cookie if required if app.config['BUI_SCOOKIE']: criteria = ( request.is_secure, request.headers.get('X-Forwarded-Proto', 'http') == 'https' ) app.config['SESSION_COOKIE_SECURE'] = \ app.config['REMEMBER_COOKIE_SECURE'] = any(criteria) if '_extra' in request.args: session['_extra'] = request.args.get('_extra') g._extra = session.get('_extra', '') @app.login_manager.user_loader def load_user(userid): """User loader callback""" if app.auth != 'none': user = app.uhandler.user(userid) if not user: return None if 'X-Language' in request.headers: language = request.headers.get('X-Language') user.language = language session['language'] = language if '_id' not in session: from flask_login import login_user # if _id is not in session, it means we loaded the user from # cache/db using the remember cookie so we need to login it login_user(user, remember=user.is_authenticated, fresh=False) _check_session(user, request) return user return None @app.login_manager.request_loader def load_user_from_request(request): """User loader from request callback""" if app.auth != 'none': user = basic_login_from_request(request, app) _check_session(user, request, True) return user @app.after_request def after_request(response): if getattr(g, 'basic_session', False): if session_manager.invalidate_current_session(): session_manager.delete_session() return response if app.demo and SENTRY_AVAILABLE: @app.errorhandler(500) def internal_server_error(error): from .ext.sentry import sentry return render_template( '500_sentry.html', event_id=g.sentry_event_id, public_dsn=sentry.client.get_public_dsn('https') ) return app
import uuid from collections import defaultdict import os import flask from flask import request, render_template, Response, stream_with_context, send_from_directory from flask_login import login_required, login_user, logout_user from Extractor import app, login_manager from Extractor.data_extractor import DataExtractor from Extractor.database import db_session from Extractor.exceptions import InvalidUsage from Extractor.forms import LoginForm, DatasetForm, VariableForm, TokenForm from Extractor.models import User, Dataset, Variable, UserToken from Extractor.utils import is_safe_url, DATE_FMT from flask_bower import Bower Bower(app) # For static javascript file serving @app.errorhandler(400) def page_not_found(error): print(error) return render_template('400.html', error=error), 400 @login_manager.user_loader def load_user(user_id): user = User.query.get(user_id) return user @app.errorhandler(InvalidUsage)
# third-party imports from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from flask_bower import Bower # local imports from config import app_config # db variable initialization db = SQLAlchemy() migrate = Migrate() bower = Bower() def create_app(config_name): app = Flask(__name__) app.config.from_object(app_config[config_name]) db.init_app(app) migrate.init_app(app, db) bower.init_app(app) from app import models from .home import home as home_blueprint app.register_blueprint(home_blueprint) return app
class FlaskJanitoo(object): def __init__(self, app=None, options=None, db=None): self._app = app self._db = db self.options = options if self.options is not None and 'conf_file' in self.options and self.options['conf_file'] is not None: logging_fileConfig(self.options['conf_file']) self._listener = None self._listener_lock = None self._sleep = 0.25 self.menu_left = [] # Bower self.bower = Bower() # Caching self.cache = Cache() def __del__(self): """ """ try: self.stop_listener() except Exception: pass def init_app(self, app, options, db=None): """ """ if app is not None: self._app = app if options is not None: self.options = options if db is not None: self._db = db if self.options is not None and 'conf_file' in self.options and self.options['conf_file'] is not None: logging_fileConfig(self.options['conf_file']) # Flask-Cache self.cache.init_app(self._app) # Flask-Bower self.bower.init_app(self._app) self._event_manager = EventManager(self._app) self._app.jinja_env.globals["emit_event"] = self._event_manager.template_emit if not hasattr(self._app, 'extensions'): self._app.extensions = {} self._app.extensions['options'] = self.options self._app.extensions['bower'] = self.bower self._app.extensions['cache'] = self.cache self._app.extensions['janitoo'] = self try: self._sleep = int(self._app.config['FLASKJANITOO_SLEEP']) if self._sleep <= 0 : self._sleep = 0.25 except KeyError: self._sleep = 0.25 except ValueError: self._sleep = 0.25 # Use the newstyle teardown_appcontext if it's available, # otherwise fall back to the request context if hasattr(self._app, 'teardown_appcontext'): self._app.teardown_appcontext(self.teardown) else: self._app.teardown_request(self.teardown) signal.signal(signal.SIGTERM, self.signal_term_handler) signal.signal(signal.SIGINT, self.signal_term_handler) self._listener_lock = threading.Lock() self.create_listener() def create_listener(self): """Create the listener on first call """ self._listener = ListenerThread(self._app, self.options) @property def listener(self): """Start the listener on first call """ self.start_listener() return self._listener def start_listener(self): """Start the listener on first call """ try: self._listener_lock.acquire() if not self._listener.is_alive(): self._listener.start() finally: self._listener_lock.release() def stop_listener(self): """Stop the listener """ try: self._listener_lock.acquire() self._listener.stop() try: self._listener.join() except RuntimeError: pass self._listener = None finally: self._listener_lock.release() def extend_blueprints(self, group): """ """ for entrypoint in iter_entry_points(group = '%s.blueprint'%'janitoo_manager'): logger.info('Add blueprint from %s', entrypoint.module_name ) bprint, url = entrypoint.load()() self._app.register_blueprint(bprint, url_prefix=url) for entrypoint in iter_entry_points(group = '%s.menu_left'%group): logger.info('Extend menu_left with %s', entrypoint.module_name ) self.menu_left.append(entrypoint.load()()) self._app.template_context_processors[None].append(lambda : dict(jnt_menu_left=self.menu_left)) def extend_network(self, group): """"Extend the network with methods found in entrypoints """ if self._listener and self._listener.network: self._listener.network.extend_from_entry_points(group) else: raise RuntimeError("Can't extend an uninitialized network") def extend_listener(self, group): """"Extend the network with methods found in entrypoints """ if self._listener: self._listener.extend_from_entry_points(group) else: raise RuntimeError("Can't extend an uninitialized listener") def signal_term_handler(self, signal, frame): """ """ logger.info("[ %s ] - Received signal %s", self.__class__.__name__, signal) if self._listener and self._listener.is_alive(): self._listener.stop() try: self._listener.join() except AssertionError: logger.exception("Catched exception in signal_term_handler: ") except RuntimeError: logger.exception("Catched exception in signal_term_handler: ") sys.exit(0) #~ def connect(self): #~ return sqlite3.connect(current_app.config['SQLITE3_DATABASE']) #~ @property #~ def backend(self): #~ ctx = stack.top #~ if ctx is not None: #~ if not hasattr(ctx, 'tinyflow_backend'): #~ ctx.tinyflow_backend = self._backend #~ return ctx.tinyflow_backend #~ #~ @property #~ def thread(self): #~ ctx = stack.top #~ if ctx is not None: #~ if not hasattr(ctx, 'tinyflow_server'): #~ ctx.tinyflow_server = self._server #~ return ctx.tinyflow_server #~ def teardown(self, exception): pass
from flask import Flask, flash, make_response, request, redirect, url_for from flask import render_template from config import Config from flask_bower import Bower from voxy.app.forms import WordCountForm from voxy.wordcount.wordcount import count_words wordcount = Flask(__name__, static_url_path='') Bower(wordcount) wordcount.debug = True wordcount.config.from_object(Config) @wordcount.route('/', methods=["GET"]) def index(): form = WordCountForm() return render_template("index.html", title="Word Count", form=form, is_init=True) _err_message_tempate = "You must submit some words to count!" _success_message_tempate = "You submitted text data with {} words" # TODO: Would like to have some kind of a word/data limit on what can be submitted. @wordcount.route('/submit', methods=["GET", "POST"]) def submit(): if request.method == "GET":
def init_extensions(app): """Registers extensions with the application.""" Bower(app)