def class_rebuilder(cls): # decorator def init(self): logger.info("[%s] Applying ID to endopoint:%s of type '%s'" % (self.__class__.__name__, name, idtype)) self.set_method_id(name, idtype) # logger.debug("New init %s %s" % (name, idtype)) super(cls, self).__init__() NewClass = Meta.metaclassing( cls, cls.__name__ + '_withid', {'__init__': init}) return NewClass
def decorated(*args, **kwargs): # Recover the auth object auth, token = self.get_auth_from_header() # Internal API 'self' reference decorated_self = Meta.get_self_reference_from_args(*args) # Handling OPTIONS forwarded to our application: # ignore headers and let go, avoid unwanted interactions with CORS if request.method != 'OPTIONS': if auth and auth.username: # case of username and password password = self.get_password_callback(auth.username) else: # case for a header token password = None # Check authentication token_fn = g._custom_auth.verify_token if not self.authenticate(token_fn, auth, password): # Clear TCP receive buffer of any pending data request.data headers = { HTTPAUTH_AUTH_HEADER: self.authenticate_header()} # Mimic the response from a normal endpoint # To use the same standards return decorated_self.force_response( errors={"Invalid token": "Received '%s'" % token}, headers=headers, code=hcodes.HTTP_BAD_UNAUTHORIZED ) # Check roles if len(roles) > 0: roles_fn = g._custom_auth.verify_roles if not self.authenticate_roles(roles_fn, roles): return decorated_self.force_response( errors={"Missing privileges": "One or more role required"}, code=hcodes.HTTP_BAD_UNAUTHORIZED ) return f(*args, **kwargs)
def create_app(name=__name__, debug=False, worker_mode=False, testing_mode=False, avoid_context=False, enable_security=True, skip_endpoint_mapping=False, **kwargs): """ Create the server istance for Flask application """ ################################################# # Flask app instance ################################################# from .confs import config microservice = Flask(name, **kwargs) ############################## # @microservice.before_first_request # def first(): # print("BEFORE THE VERY FIRST REQUEST", g) # @microservice.before_request # def before(): # print("BEFORE EVERY REQUEST...") # @microservice.after_request # def after(response): # print("AFTER EVERY REQUEST...") # return response ############################## # Disable security if launching celery workers if worker_mode: enable_security = False # Set app internal testing mode if create_app received the parameter if testing_mode: microservice.config['TESTING'] = testing_mode ############################## # Flask configuration from config file microservice.config.from_object(config) if ENVVAR_DEBUG is not None: try: tmp = int(ENVVAR_DEBUG) == 1 except: tmp = str(ENVVAR_DEBUG).lower() == 'true' debug = tmp # bool(tmp) microservice.config['DEBUG'] = debug # Set the new level of debugging logger = get_logger(__name__, debug) logger.info("FLASKING! Created application") ############################## if PRODUCTION: logger.info("Production server ON") ## // TO FIX or CHECK # # Check and use a random file a secret key. # install_secret_key(microservice) # probably useless # # http://stackoverflow.com/a/26636880/2114395 # microservice.config.update( # dict(PREFERRED_URL_SCHEME = 'https') # ) # # To enable exceptions printing inside uWSGI # # http://stackoverflow.com/a/17839750/2114395 # from werkzeug.debug import DebuggedApplication # app.wsgi_app = DebuggedApplication(app.wsgi_app, True) ################################################# # Other components ################################################# ############################## # Cors from .cors import cors cors.init_app(microservice) logger.info("FLASKING! Injected CORS") ############################## # DATABASE/SERVICEs CHECKS from .resources.services.detect import services as internal_services for service, myclass in internal_services.items(): logger.info("Available service %s" % service) myclass(check_connection=True, app=microservice) ############################## # Enabling our internal Flask customized response from .response import InternalResponse microservice.response_class = InternalResponse ############################## # Flask security if enable_security: # Dynamically load the authentication service meta = Meta() module_base = __package__ + ".resources.services.authentication" auth_service = os.environ.get('BACKEND_AUTH_SERVICE', '') module_name = module_base + '.' + auth_service logger.debug("Trying to load the module %s" % module_name) module = meta.get_module_from_string(module_name) init_auth = create_auth_instance( module, internal_services, microservice) # Global namespace inside the Flask server @microservice.before_request def enable_authentication_per_request(): """ Save auth object """ # Authentication the right (per-instance) way custom_auth = create_auth_instance( module, internal_services, microservice) # Save globally across the code g._custom_auth = custom_auth # OLD BAD # def enable_global_authentication(): # """ Save auth object """ # g._custom_auth = custom_auth # Enabling also OAUTH library from .oauth import oauth oauth.init_app(microservice) logger.info("FLASKING! Injected security internal module") if not worker_mode: # Global namespace inside the Flask server @microservice.before_request def enable_global_services(): """ Save all databases/services """ g._services = internal_services ############################## # Restful plugin if not skip_endpoint_mapping: from .rest import Api, EndpointsFarmer, create_endpoints # Defining AUTOMATIC Resources current_endpoints = \ create_endpoints(EndpointsFarmer(Api), enable_security, debug) # Restful init of the app current_endpoints.rest_api.init_app(microservice) ############################## # Init objects inside the app context if not avoid_context: with microservice.app_context(): # Set global objects for celery workers if worker_mode: from commons.globals import mem mem.services = internal_services # Note: # Databases are already initialized inside the instances farm # Outside of the context # p.s. search inside this file for 'myclass(' # Init users/roles for Security if enable_security: # custom_auth.setup_secret(microservice.config['SECRET_KEY']) # custom_auth.init_users_and_roles() init_auth.init_users_and_roles() # Allow a custom method for mixed services init try: from .resources.custom import services as custom_services custom_services.init(internal_services, enable_security) except: logger.debug("No custom init available for mixed services") ############################## # Logging responses @microservice.after_request def log_response(response): data = handle_log_output(request.data) # Limit the parameters string size, sometimes it's too big for k in data: # print("K", k, "DATA", data) try: if not isinstance(data[k], str): continue if len(data[k]) > MAX_CHAR_LEN: data[k] = data[k][:MAX_CHAR_LEN] + "..." except IndexError: pass logger.info("{} {} {} {}".format( request.method, request.url, data, response)) return response ############################## # Enabling user callbacks after a request @microservice.after_request def call_after_request_callbacks(response): for callback in getattr(g, 'after_request_callbacks', ()): callback(response) return response ############################## # App is ready return microservice
""" # from restapi.resources.services.celery.tasks import MyCelery from commons.services.celery import celery_app from restapi.server import create_app from commons.meta import Meta from commons.logs import get_logger logger = get_logger(__name__) ################################################ # Reload Flask app code also for the worker # This is necessary to have the app context available app = create_app(worker_mode=True, debug=True) # Recover celery app with current app # celery_app = MyCelery(app)._current # celery_app = MyCelery(app)._current logger.debug("Celery %s" % celery_app) ################################################ # Import tasks modules to make sure all tasks are avaiable meta = Meta() main_package = "commons.tasks." # Base tasks submodules = meta.import_submodules_from_package(main_package + "base") # Custom tasks submodules = meta.import_submodules_from_package(main_package + "custom")
def single_rest(self, config_file): meta = Meta() resources = [] sections = {} if not os.path.exists(config_file): logger.warning("File '%s' does not exist! Skipping." % config_file) return resources ######################### # Read the configuration inside this init file # JSON CASE try: sections = self.read_complex_config(config_file) except: # json.commentjson.JSONLibraryException: logger.critical("Format error!\n" + "'%s' file is not in JSON format" % config_file) exit(1) if 'apis' not in sections: logger.critical( "Section 'apis' not found in '%s' file" % config_file ) exit(1) ######################### # Use sections found for section, items in iteritems(sections['apis']): logger.debug("Configuration read: {Section: " + section + "}") module = meta.get_module_from_string( __package__ + '.resources.custom.' + section) # Skip what you cannot use if module is None: logger.warning("Could not find module '%s'..." % section) continue for classname, endpoints in iteritems(items): myclass = meta.get_class_from_string(classname, module) # Again skip if myclass is None: continue else: logger.debug("REST! Found resource: " + section + '.' + classname) # Get the best endpoint comparing inside against configuration instance = myclass() oldendpoint, endkey, endtype = instance.get_endpoint() if len(endpoints) < 1: endpoints = [oldendpoint] endpoint_id = None if endkey is not None and endtype is not None: endpoint_id = endtype + ':' + endkey resources.append((myclass, instance, endpoints, endpoint_id)) return resources