From 6e02ed5c06e105d2e9eeb8d80cc5d2e0903f6c5f Mon Sep 17 00:00:00 2001 From: LV-426 Date: Sun, 4 Jan 2015 17:23:35 +0200 Subject: rename rhizi_server -> rz_server --- src/server/rhizi_server.py | 283 ---------------------------------------- src/server/rhizi_server_fcgi.py | 26 ---- src/server/rz_server.py | 283 ++++++++++++++++++++++++++++++++++++++++ src/server/rz_server_fcgi.py | 26 ++++ 4 files changed, 309 insertions(+), 309 deletions(-) delete mode 100644 src/server/rhizi_server.py delete mode 100755 src/server/rhizi_server_fcgi.py create mode 100644 src/server/rz_server.py create mode 100755 src/server/rz_server_fcgi.py (limited to 'src/server') diff --git a/src/server/rhizi_server.py b/src/server/rhizi_server.py deleted file mode 100644 index 77a1ae7e..00000000 --- a/src/server/rhizi_server.py +++ /dev/null @@ -1,283 +0,0 @@ -#!/usr/bin/python - -import logging -import json -import util -import os -import neo4j_util -import argparse -import db_controller as dbc -import flask -import crypt_util -import re - -from flask import Flask -from flask import Response -from flask import session -from flask import redirect -from flask import request -from flask import send_from_directory - -from functools import wraps -from rz_mesh import init_ws_interface -from rz_kernel import RZ_Kernel -import rz_api -import rz_api_rest - -class Config(object): - """ - rhizi-server configuration - - TODO: config option documentation - - htpasswd_path - listen_address - listen_port - neo4j_url - root_path - """ - - @staticmethod - def init_from_file(file_path): - - if False == os.path.exists(file_path): - raise Exception('config file not found: ' + file_path) - - # apply defaults - cfg = {} - cfg['access_control'] = True - cfg['config_dir'] = os.path.abspath(os.path.dirname(file_path)) # bypass prop restriction - cfg['development_mode'] = False - cfg['listen_address'] = '127.0.0.1' - cfg['listen_port'] = 8080 - cfg['root_path'] = os.getcwd() - cfg['static_url_path'] = '/static' - - # Flask keys - cfg['SECRET_KEY'] = '' - - with open(file_path, 'r') as f: - for line in f: - if re.match('(^#)|(\s+$)', line): - continue - - kv_arr = line.split('=') - if 2 != len(kv_arr): - raise Exception('failed to parse config line: ' + line) - - k, v = map(str.strip, kv_arr) - - if None != cfg.get(k): - # apply type conversion based on default value type - type_f = type(cfg[k]) - if bool == type_f: - v = v in ("True", "true") # workaround bool('false') = True - else: - v = type_f(v) - - # [!] we can't use k.lower() as we are loading Flask configuration - # keys which are expected to be capitalized - cfg[k] = v - - ret = Config() - ret.__dict__ = cfg # allows setting of @property attributes - - # validate config - if False == os.path.isabs(ret.root_path): - ret.root_path = os.path.abspath(ret.root_path) - - return ret - - def __str__(self): - return '\n'.join('%s: %s' % (k, v) for k, v in self.__dict__.items()) - - @property - def db_base_url(self): - return self.neo4j_url - - @property - def tx_api_path(self): - return '/db/data/transaction' - - @property - def config_dir_path(self): - return self.config_dir - - @property - def secret_key(self): - return self.SECRET_KEY - -class FlaskExt(Flask): - """ - Flask server customization - """ - - def __init__(self, import_name, *args, **kwargs): - """ - reserved for future use - """ - super(FlaskExt, self).__init__(import_name, *args, **kwargs) - - def before_request(self, *args, **kwargs): - # TODO impl - pass - - def make_default_options_response(self): - ret = Flask.make_default_options_response(self) - - ret.headers['Access-Control-Allow-Origin'] = 'http://rhizi.net' - ret.headers['Access-Control-Allow-Headers'] = "Accept, Authorization, Content-Type, Origin" - ret.headers['Access-Control-Allow-Credentials'] = 'true' - - # ret.headers['Access-Control-Allow-Methods'] = ', '.join(m_list) - return ret - -def init_log(cfg): - """ - init log file, location derived from configuration - """ - log = logging.getLogger('rhizi') - log.setLevel(logging.DEBUG) - log_handler_c = logging.StreamHandler() - log_handler_f = logging.FileHandler(cfg.log_path) - - log.addHandler(log_handler_c) - log.addHandler(log_handler_f) - return log - -def init_rest_interface(cfg, flask_webapp): - """ - Initialize REST interface - """ - - def rest_entry(path, f, flask_args={'methods': ['POST']}): - return (path, f, flask_args) - - def redirect_entry(path, path_to, flask_args): - def redirector(): - return redirect(path_to, code=302) - return (path, redirector, flask_args) - - def dev_mode__resend_from_static(static_url): - """ - redirect static links while in dev mode: - - /res/ -> - """ - static_folder = flask.current_app.static_folder - - new_req_path = None - if request.path.startswith('/res/'): - # turn absolute/res/... URLs to static-folder relative - new_req_path = request.path.replace('/res/', '') - return send_from_directory(static_folder, new_req_path) - - def login_decorator(f): - """ - [!] security boundary: asserd logged-in user before executing REST api call - """ - @wraps(f) - def wrapped_function(*args, **kw): - if not 'username' in session: - return redirect('/login') - return f(*args, **kw) - - return wrapped_function - - rest_entry_set = [ - redirect_entry('/', '/index', {'methods': ['GET']}), - rest_entry('/add/node-set' , rhizi_api.add_node_set), - rest_entry('/graph/clone', rhizi_api.rz_clone), - rest_entry('/graph/diff-commit-set', rhizi_api.diff_commit__set), - rest_entry('/graph/diff-commit-topo', rhizi_api.diff_commit__topo), - rest_entry('/graph/diff-commit-attr', rhizi_api.diff_commit__attr), - rest_entry('/graph/diff-commit-vis', rhizi_api.diff_commit__vis), - rest_entry('/index', rhizi_api.index, {'methods': ['GET']}), - rest_entry('/load/node-set-by-id', rhizi_api.load_node_set_by_id_attr), - rest_entry('/load/link-set/by_link_ptr_set', rhizi_api.load_link_set_by_link_ptr_set), - rest_entry('/login', rhizi_api.login, {'methods': ['GET', 'POST']}), - rest_entry('/logout', rhizi_api.logout, {'methods': ['GET', 'POST']}), - rest_entry('/match/node-set', rhizi_api.match_node_set_by_attr_filter_map), - rest_entry('/monitor/server-info', rhizi_api.monitor__server_info), - ] - - if cfg.development_mode: - dev_path_set = ['/static', '/res'] - rest_dev_entry_set = [] - for dev_path in dev_path_set: - rest_dev_entry_set.append(rest_entry(dev_path + '/', - dev_mode__resend_from_static, - {'methods': ['GET']})) - rest_entry_set += rest_dev_entry_set - - for re_entry in rest_entry_set: - rest_path, f, flask_args = re_entry - - if cfg.access_control and '/login' != rest_path: - # currently require login on all but /login paths - f = login_decorator(f) - - # [!] order seems important - apply route decorator last - route_dec = flask_webapp.route(rest_path, **flask_args) - f = route_dec(f) - - flask_webapp.f = f # assign decorated function - -def init_webapp(cfg, kernel, db_ctl=None): - """ - Initialize webapp: - - call init_rest_interface() - """ - root_path = cfg.root_path - - webapp = FlaskExt(__name__, - static_folder='static', - template_folder=os.path.join(root_path, 'templates'), - static_url_path=cfg.static_url_path) - webapp.config.from_object(cfg) - webapp.root_path = root_path # for some reason calling config.from_xxx() does not have effect - - if None == db_ctl: - db_ctl = dbc.DB_Controller(cfg) - rz_api.db_ctl = db_ctl - rz_api_rest.db_ctl = db_ctl - - webapp.rz_config = cfg - webapp.kernel = kernel - - init_rest_interface(cfg, webapp) - return webapp - -def init_config(cfg_dir): - cfg_path = os.path.join(cfg_dir, 'rhizi-server.conf') - cfg = Config.init_from_file(cfg_path) - return cfg - -def init_pw_db(cfg, user_pw_list_file): - pass # TODO: stub, will move to tools/ - - -if __name__ == "__main__": - - p = argparse.ArgumentParser(description='rhizi-server') - p.add_argument('--config-dir', help='path to Rhizi config dir', default='res/etc') - p.add_argument('--init-htpasswd-db', help='init login htpasswd db', action='store_const', const=True) - p.add_argument('--htpasswd-init-file', help='htpasswd db initialization file in \'user,pw\' format') - args = p.parse_args() - - cfg = init_config(args.config_dir) - log = init_log(cfg) - - cfg_indent_str = ' ' + str(cfg).replace('\n', '\n ') - log.debug('loaded configuration:\n%s' % cfg_indent_str) # print indented - if False == cfg.access_control: - log.warn('access control disabled, all-granted access set on all URLs') - - if args.init_htpasswd_db: - init_pw_db(cfg, args.htpasswd_init_file) - exit(0) - - kernel = RZ_Kernel() - webapp = init_webapp(cfg, kernel) - ws_srv = init_ws_interface(cfg, kernel, webapp) - - ws_srv.serve_forever() diff --git a/src/server/rhizi_server_fcgi.py b/src/server/rhizi_server_fcgi.py deleted file mode 100755 index b3985ee1..00000000 --- a/src/server/rhizi_server_fcgi.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/python - -from flup.server.fcgi import WSGIServer -import os -import sys -import cgitb -import rhizi_server - -from rz_kernel import RZ_Kernel - -# sys.path.insert(0, '/srv/www/rhizi/rhizi.net/src-py') - -# enable debugging -cgitb.enable() - -if __name__ == '__main__': - cfg = rhizi_server.init_config(cfg_dir='/etc/rhizi') - log = rhizi_server.init_log(cfg) - - kernel = RZ_Kernel() - webapp = rhizi_server.init_webapp(cfg, kernel) - rhizi_server.init_rest_interface(cfg, webapp) - - log.info('launching webapp via flup.server.fcgi.WSGIServer') - - WSGIServer(webapp).run() diff --git a/src/server/rz_server.py b/src/server/rz_server.py new file mode 100644 index 00000000..77a1ae7e --- /dev/null +++ b/src/server/rz_server.py @@ -0,0 +1,283 @@ +#!/usr/bin/python + +import logging +import json +import util +import os +import neo4j_util +import argparse +import db_controller as dbc +import flask +import crypt_util +import re + +from flask import Flask +from flask import Response +from flask import session +from flask import redirect +from flask import request +from flask import send_from_directory + +from functools import wraps +from rz_mesh import init_ws_interface +from rz_kernel import RZ_Kernel +import rz_api +import rz_api_rest + +class Config(object): + """ + rhizi-server configuration + + TODO: config option documentation + + htpasswd_path + listen_address + listen_port + neo4j_url + root_path + """ + + @staticmethod + def init_from_file(file_path): + + if False == os.path.exists(file_path): + raise Exception('config file not found: ' + file_path) + + # apply defaults + cfg = {} + cfg['access_control'] = True + cfg['config_dir'] = os.path.abspath(os.path.dirname(file_path)) # bypass prop restriction + cfg['development_mode'] = False + cfg['listen_address'] = '127.0.0.1' + cfg['listen_port'] = 8080 + cfg['root_path'] = os.getcwd() + cfg['static_url_path'] = '/static' + + # Flask keys + cfg['SECRET_KEY'] = '' + + with open(file_path, 'r') as f: + for line in f: + if re.match('(^#)|(\s+$)', line): + continue + + kv_arr = line.split('=') + if 2 != len(kv_arr): + raise Exception('failed to parse config line: ' + line) + + k, v = map(str.strip, kv_arr) + + if None != cfg.get(k): + # apply type conversion based on default value type + type_f = type(cfg[k]) + if bool == type_f: + v = v in ("True", "true") # workaround bool('false') = True + else: + v = type_f(v) + + # [!] we can't use k.lower() as we are loading Flask configuration + # keys which are expected to be capitalized + cfg[k] = v + + ret = Config() + ret.__dict__ = cfg # allows setting of @property attributes + + # validate config + if False == os.path.isabs(ret.root_path): + ret.root_path = os.path.abspath(ret.root_path) + + return ret + + def __str__(self): + return '\n'.join('%s: %s' % (k, v) for k, v in self.__dict__.items()) + + @property + def db_base_url(self): + return self.neo4j_url + + @property + def tx_api_path(self): + return '/db/data/transaction' + + @property + def config_dir_path(self): + return self.config_dir + + @property + def secret_key(self): + return self.SECRET_KEY + +class FlaskExt(Flask): + """ + Flask server customization + """ + + def __init__(self, import_name, *args, **kwargs): + """ + reserved for future use + """ + super(FlaskExt, self).__init__(import_name, *args, **kwargs) + + def before_request(self, *args, **kwargs): + # TODO impl + pass + + def make_default_options_response(self): + ret = Flask.make_default_options_response(self) + + ret.headers['Access-Control-Allow-Origin'] = 'http://rhizi.net' + ret.headers['Access-Control-Allow-Headers'] = "Accept, Authorization, Content-Type, Origin" + ret.headers['Access-Control-Allow-Credentials'] = 'true' + + # ret.headers['Access-Control-Allow-Methods'] = ', '.join(m_list) + return ret + +def init_log(cfg): + """ + init log file, location derived from configuration + """ + log = logging.getLogger('rhizi') + log.setLevel(logging.DEBUG) + log_handler_c = logging.StreamHandler() + log_handler_f = logging.FileHandler(cfg.log_path) + + log.addHandler(log_handler_c) + log.addHandler(log_handler_f) + return log + +def init_rest_interface(cfg, flask_webapp): + """ + Initialize REST interface + """ + + def rest_entry(path, f, flask_args={'methods': ['POST']}): + return (path, f, flask_args) + + def redirect_entry(path, path_to, flask_args): + def redirector(): + return redirect(path_to, code=302) + return (path, redirector, flask_args) + + def dev_mode__resend_from_static(static_url): + """ + redirect static links while in dev mode: + - /res/ -> + """ + static_folder = flask.current_app.static_folder + + new_req_path = None + if request.path.startswith('/res/'): + # turn absolute/res/... URLs to static-folder relative + new_req_path = request.path.replace('/res/', '') + return send_from_directory(static_folder, new_req_path) + + def login_decorator(f): + """ + [!] security boundary: asserd logged-in user before executing REST api call + """ + @wraps(f) + def wrapped_function(*args, **kw): + if not 'username' in session: + return redirect('/login') + return f(*args, **kw) + + return wrapped_function + + rest_entry_set = [ + redirect_entry('/', '/index', {'methods': ['GET']}), + rest_entry('/add/node-set' , rhizi_api.add_node_set), + rest_entry('/graph/clone', rhizi_api.rz_clone), + rest_entry('/graph/diff-commit-set', rhizi_api.diff_commit__set), + rest_entry('/graph/diff-commit-topo', rhizi_api.diff_commit__topo), + rest_entry('/graph/diff-commit-attr', rhizi_api.diff_commit__attr), + rest_entry('/graph/diff-commit-vis', rhizi_api.diff_commit__vis), + rest_entry('/index', rhizi_api.index, {'methods': ['GET']}), + rest_entry('/load/node-set-by-id', rhizi_api.load_node_set_by_id_attr), + rest_entry('/load/link-set/by_link_ptr_set', rhizi_api.load_link_set_by_link_ptr_set), + rest_entry('/login', rhizi_api.login, {'methods': ['GET', 'POST']}), + rest_entry('/logout', rhizi_api.logout, {'methods': ['GET', 'POST']}), + rest_entry('/match/node-set', rhizi_api.match_node_set_by_attr_filter_map), + rest_entry('/monitor/server-info', rhizi_api.monitor__server_info), + ] + + if cfg.development_mode: + dev_path_set = ['/static', '/res'] + rest_dev_entry_set = [] + for dev_path in dev_path_set: + rest_dev_entry_set.append(rest_entry(dev_path + '/', + dev_mode__resend_from_static, + {'methods': ['GET']})) + rest_entry_set += rest_dev_entry_set + + for re_entry in rest_entry_set: + rest_path, f, flask_args = re_entry + + if cfg.access_control and '/login' != rest_path: + # currently require login on all but /login paths + f = login_decorator(f) + + # [!] order seems important - apply route decorator last + route_dec = flask_webapp.route(rest_path, **flask_args) + f = route_dec(f) + + flask_webapp.f = f # assign decorated function + +def init_webapp(cfg, kernel, db_ctl=None): + """ + Initialize webapp: + - call init_rest_interface() + """ + root_path = cfg.root_path + + webapp = FlaskExt(__name__, + static_folder='static', + template_folder=os.path.join(root_path, 'templates'), + static_url_path=cfg.static_url_path) + webapp.config.from_object(cfg) + webapp.root_path = root_path # for some reason calling config.from_xxx() does not have effect + + if None == db_ctl: + db_ctl = dbc.DB_Controller(cfg) + rz_api.db_ctl = db_ctl + rz_api_rest.db_ctl = db_ctl + + webapp.rz_config = cfg + webapp.kernel = kernel + + init_rest_interface(cfg, webapp) + return webapp + +def init_config(cfg_dir): + cfg_path = os.path.join(cfg_dir, 'rhizi-server.conf') + cfg = Config.init_from_file(cfg_path) + return cfg + +def init_pw_db(cfg, user_pw_list_file): + pass # TODO: stub, will move to tools/ + + +if __name__ == "__main__": + + p = argparse.ArgumentParser(description='rhizi-server') + p.add_argument('--config-dir', help='path to Rhizi config dir', default='res/etc') + p.add_argument('--init-htpasswd-db', help='init login htpasswd db', action='store_const', const=True) + p.add_argument('--htpasswd-init-file', help='htpasswd db initialization file in \'user,pw\' format') + args = p.parse_args() + + cfg = init_config(args.config_dir) + log = init_log(cfg) + + cfg_indent_str = ' ' + str(cfg).replace('\n', '\n ') + log.debug('loaded configuration:\n%s' % cfg_indent_str) # print indented + if False == cfg.access_control: + log.warn('access control disabled, all-granted access set on all URLs') + + if args.init_htpasswd_db: + init_pw_db(cfg, args.htpasswd_init_file) + exit(0) + + kernel = RZ_Kernel() + webapp = init_webapp(cfg, kernel) + ws_srv = init_ws_interface(cfg, kernel, webapp) + + ws_srv.serve_forever() diff --git a/src/server/rz_server_fcgi.py b/src/server/rz_server_fcgi.py new file mode 100755 index 00000000..fd871abb --- /dev/null +++ b/src/server/rz_server_fcgi.py @@ -0,0 +1,26 @@ +#!/usr/bin/python + +from flup.server.fcgi import WSGIServer +import os +import sys +import cgitb +import rz_server + +from rz_kernel import RZ_Kernel + +# sys.path.insert(0, '/srv/www/rhizi/rhizi.net/src-py') + +# enable debugging +cgitb.enable() + +if __name__ == '__main__': + cfg = rz_server.init_config(cfg_dir='/etc/rhizi') + log = rz_server.init_log(cfg) + + kernel = RZ_Kernel() + webapp = rz_server.init_webapp(cfg, kernel) + rz_server.init_rest_interface(cfg, webapp) + + log.info('launching webapp via flup.server.fcgi.WSGIServer') + + WSGIServer(webapp).run() -- cgit v1.3.1