Example #1
0
    def test_default_config(self):
        """ Ensure we have expected default parameters """
        app = w.create_app()

        keys = [
            'ARA_AUTOCREATE_DATABASE',
            'ARA_DIR',
            'ARA_ENABLE_DEBUG_VIEW',
            'ARA_LOG_FILE',
            'ARA_LOG_FORMAT',
            'ARA_LOG_LEVEL',
            'ARA_PLAYBOOK_OVERRIDE',
            'ARA_PLAYBOOK_PER_PAGE',
            'ARA_RESULT_PER_PAGE',
        ]

        for key in keys:
            self.assertEqual(c.DEFAULTS[key], app.config[key])

        self.assertEqual(c.DEFAULTS['ARA_DATABASE'],
                         app.config['SQLALCHEMY_DATABASE_URI'])
        self.assertEqual(c.DEFAULTS['ARA_SQL_DEBUG'],
                         app.config['SQLALCHEMY_ECHO'])
        self.assertEqual(c.DEFAULTS['ARA_TMP_DIR'],
                         os.path.split(app.config['ARA_TMP_DIR'])[:-1][0])
        self.assertEqual(c.DEFAULTS['ARA_IGNORE_MIMETYPE_WARNINGS'],
                         app.config['FREEZER_IGNORE_MIMETYPE_WARNINGS'])
Example #2
0
def application(environ, start_response):
    global app

    # Load virtualenv if necessary
    if (int(environ.get('ARA_WSGI_USE_VIRTUALENV', 0)) == 1 and
       environ.get('ARA_WSGI_VIRTUALENV_PATH')):
        # Backwards compatibility, we did not always suffix activate_this.py
        activate_this = environ.get('ARA_WSGI_VIRTUALENV_PATH')
        if 'activate_this.py' not in activate_this:
            activate_this = os.path.join(activate_this, 'bin/activate_this.py')
        if six.PY2:
            execfile(activate_this, dict(__file__=activate_this))  # nosec
        else:
            exec(open(activate_this).read())  # nosec

    if 'ANSIBLE_CONFIG' in environ:
        os.environ['ANSIBLE_CONFIG'] = environ['ANSIBLE_CONFIG']
    else:
        if 'ANSIBLE_CONFIG' not in os.environ:
            log.warning('ANSIBLE_CONFIG environment variable not found.')

    from ara.webapp import create_app  # flake8: noqa
    from flask import current_app  # flake8: noqa
    with app_making_lock:
        if app is None:
            app = create_app()
    with app.app_context():
        return current_app(environ, start_response)
Example #3
0
def application(environ, start_response):
    request = environ['REQUEST_URI']
    match = re.search('/(?P<path>.*/{}/)'.format(DATABASE_DIRECTORY), request)
    if not match:
        return bad_request(environ, start_response,
                           'No "/{}/" in URL.'.format(DATABASE_DIRECTORY))

    path = os.path.abspath(os.path.join(LOG_ROOT, match.group('path')))

    # Ensure we don't escape outside LOG_ROOT and we are looking at a
    # valid directory
    if not path.startswith(LOG_ROOT) or not os.path.isdir(path):
        logger.error('Directory access violation: %s' % path)
        return bad_request(environ, start_response, 'No directory found.')

    database = os.path.join(path, 'ansible.sqlite')
    if not os.path.isfile(database):
        return bad_request(environ, start_response, 'No ARA database found.')

    # ARA and Ansible (when loading configuration) both expect a directory
    # they are able to write to, this can be safely discarded.
    # Nothing is read from here and there is therefore no security risks.
    # It needs to be at a known location in order to be able to clean it up
    # so it doesn't accumulate needless directories and files.
    # TODO: ARA 1.0 no longer requires temporary directories, clean this up.
    tmpdir = '/tmp/ara_wsgi_sqlite'  # nosec
    if os.path.exists(tmpdir):
        # Periodically delete this directory to avoid accumulating directories
        # and files endlessly
        now = time.time()
        if now - TMPDIR_MAX_AGE > os.path.getmtime(tmpdir):
            shutil.rmtree(tmpdir, ignore_errors=True)
    os.environ['ANSIBLE_LOCAL_TEMP'] = os.path.join(tmpdir, '.ansible')
    os.environ['ARA_DIR'] = os.path.join(tmpdir, '.ara')

    # Path to the ARA database
    os.environ['ARA_DATABASE'] = 'sqlite:///{}'.format(database)
    # The intent is that we are dealing with databases that already exist.
    # Therefore, we're not really interested in creating the database and
    # making sure that the SQL migrations are done. Toggle that off.
    # This needs to be a string, we're setting an environment variable
    os.environ['ARA_AUTOCREATE_DATABASE'] = 'false'

    msg = 'Request {request} mapped to {database} with root {root}'.format(
        request=request,
        database='sqlite:///{}'.format(database),
        root=match.group('path'))
    logger.debug(msg)

    from ara.webapp import create_app
    try:
        app = create_app()
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///{}'.format(database)
        app.config['APPLICATION_ROOT'] = match.group('path')
        return app(environ, start_response)
    except Exception as e:
        # We're staying relatively vague on purpose to avoid disclosure
        logger.error('ARA bootstrap failure for %s: %s' % (database, str(e)))
        return bad_request(environ, start_response, 'ARA bootstrap failure.')
Example #4
0
    def run(self, tmp=None, task_vars=None):
        if task_vars is None:
            task_vars = dict()

        if not HAS_ARA:
            result = {
                'failed': True,
                'msg': 'ARA is required to run this module.'
            }
            return result

        for arg in self._task.args:
            if arg not in self.VALID_ARGS:
                result = {
                    'failed': True,
                    'msg': '{0} is not a valid option.'.format(arg)
                }
                return result

        result = super(ActionModule, self).run(tmp, task_vars)

        playbook_id = self._task.args.get('playbook', None)
        key = self._task.args.get('key', None)

        required = ['key']
        for parameter in required:
            if not self._task.args.get(parameter):
                result['failed'] = True
                result['msg'] = '{0} parameter is required'.format(parameter)
                return result

        app = create_app()
        if not current_app:
            context = app.app_context()
            context.push()

        if playbook_id is None:
            # Retrieve playbook_id from the cached context
            playbook_id = current_app._cache['playbook']

        try:
            data = self.get_key(playbook_id, key)
            if data:
                result['key'] = data.key
                result['value'] = data.value
                result['type'] = data.type
                result['playbook_id'] = data.playbook_id
            msg = 'Sucessfully read data for the key {0}'.format(data.key)
            result['msg'] = msg
        # TODO: Do a better job for handling exception
        except Exception as e:
            result['key'] = None
            result['value'] = None
            result['type'] = None
            result['playbook_id'] = None
            result['failed'] = True
            msg = 'Could not read data for key {0}: {1}'.format(key, str(e))
            result['msg'] = msg
        return result
Example #5
0
    def run(self, tmp=None, task_vars=None):
        if task_vars is None:
            task_vars = dict()

        if not HAS_ARA:
            result = {
                'failed': True,
                'msg': 'ARA is required to run this module.'
            }
            return result

        for arg in self._task.args:
            if arg not in self.VALID_ARGS:
                result = {
                    'failed': True,
                    'msg': '{0} is not a valid option.'.format(arg)
                }
                return result

        result = super(ActionModule, self).run(tmp, task_vars)

        playbook_id = self._task.args.get('playbook', None)
        key = self._task.args.get('key', None)

        required = ['key']
        for parameter in required:
            if not self._task.args.get(parameter):
                result['failed'] = True
                result['msg'] = '{0} parameter is required'.format(parameter)
                return result

        app = create_app()
        if not current_app:
            context = app.app_context()
            context.push()

        if playbook_id is None:
            # Retrieve playbook_id from the cached context
            playbook_id = current_app._cache['playbook']

        try:
            data = self.get_key(playbook_id, key)
            if data:
                result['key'] = data.key
                result['value'] = data.value
                result['type'] = data.type
                result['playbook_id'] = data.playbook_id
            msg = 'Sucessfully read data for the key {0}'.format(data.key)
            result['msg'] = msg
        # TODO: Do a better job for handling exception
        except Exception as e:
            result['key'] = None
            result['value'] = None
            result['type'] = None
            result['playbook_id'] = None
            result['failed'] = True
            msg = 'Could not read data for key {0}: {1}'.format(key, str(e))
            result['msg'] = msg
        return result
Example #6
0
    def prepare_to_run_command(self, cmd):
        log.debug('prepare_to_run_command: %s', cmd.__class__.__name__)

        # Note: cliff uses self.app for itself, this gets folded back into
        # self.app.ara
        self.ara = create_app()
        if not current_app:
            self.ara_context = self.ara.app_context()
            self.ara_context.push()
Example #7
0
    def prepare_to_run_command(self, cmd):
        self.LOG.debug('prepare_to_run_command %s', cmd.__class__.__name__)

        # Note: cliff uses self.app for itself, this gets folded back into
        # self.app.ara
        self.ara = create_app()
        if not current_app:
            self.ara_context = self.ara.app_context()
            self.ara_context.push()
Example #8
0
    def setUp(self):
        self.config = {'SQLALCHEMY_DATABASE_URI': 'sqlite://', 'TESTING': True}

        self.app = w.create_app(self)
        self.app_context = self.app.app_context()
        self.app_context.push()
        self.client = self.app.test_client()

        m.db.create_all()
Example #9
0
def application(environ, start_response):
    if 'ANSIBLE_CONFIG' in environ:
        os.environ['ANSIBLE_CONFIG'] = environ['ANSIBLE_CONFIG']
    else:
        if 'ANSIBLE_CONFIG' not in os.environ:
            log.warn('ANSIBLE_CONFIG environment variable not found.')

    from ara.webapp import create_app
    app = create_app()
    return app(environ, start_response)
Example #10
0
def application(environ, start_response):
    if 'ANSIBLE_CONFIG' in environ:
        os.environ['ANSIBLE_CONFIG'] = environ['ANSIBLE_CONFIG']
    else:
        if 'ANSIBLE_CONFIG' not in os.environ:
            log.warn('ANSIBLE_CONFIG environment variable not found.')
    with app_making_lock:
        if app is None:
            app = create_app()
    with app.app_context():
        return current_app(environ, start_response)
Example #11
0
    def setUp(self):
        self.config = {"SQLALCHEMY_DATABASE_URI": "sqlite://", "TESTING": True}

        self.app = w.create_app(self)
        self.client = self.app.test_client()

        if not flask.current_app:
            ctx = self.app.app_context()
            ctx.push()

        m.db.create_all()
Example #12
0
    def setUp(self):
        # TODO: Fix this, it's not used in create_app() and makes the databases
        # live on the filesystem rather than memory.
        self.config = {'SQLALCHEMY_DATABASE_URI': 'sqlite://', 'TESTING': True}

        self.app = w.create_app()
        self.app_context = self.app.app_context()
        self.app_context.push()
        self.client = self.app.test_client()

        m.db.create_all()
Example #13
0
def application(environ, start_response):
    if 'ANSIBLE_CONFIG' in environ:
        os.environ['ANSIBLE_CONFIG'] = environ['ANSIBLE_CONFIG']
    else:
        if 'ANSIBLE_CONFIG' not in os.environ:
            log.warn('ANSIBLE_CONFIG environment variable not found.')

    app = create_app()
    if not current_app:
        ctx = app.app_context()
        ctx.push()
        return app(environ, start_response)
    else:
        return current_app(environ, start_response)
Example #14
0
    def setUp(self):
        # TODO: Fix this, it's not used in create_app() and makes the databases
        # live on the filesystem rather than memory.
        self.config = {
            'SQLALCHEMY_DATABASE_URI': 'sqlite://',
            'TESTING': True
        }

        self.app = w.create_app()
        self.app_context = self.app.app_context()
        self.app_context.push()
        self.client = self.app.test_client()

        m.db.create_all()
Example #15
0
    def test_default_config(self):
        """ Ensure we have expected default parameters """
        app = w.create_app()

        self.assertEqual(app.config['ARA_DIR'], c.DEFAULT_ARA_DIR)
        self.assertEqual(
            os.path.split(app.config['ARA_TMP_DIR'])[:-1][0],
            c.DEFAULT_ARA_TMP_DIR)
        self.assertEqual(app.config['ARA_LOG_FILE'], c.DEFAULT_ARA_LOG_FILE)
        self.assertEqual(app.config['ARA_LOG_LEVEL'], c.DEFAULT_ARA_LOG_LEVEL)
        self.assertEqual(app.config['ARA_LOG_FORMAT'],
                         c.DEFAULT_ARA_LOG_FORMAT)
        self.assertEqual(app.config['ARA_PATH_MAX'], c.DEFAULT_ARA_PATH_MAX)
        self.assertEqual(app.config['SQLALCHEMY_ECHO'],
                         c.DEFAULT_ARA_SQL_DEBUG)
        self.assertEqual(app.config['SQLALCHEMY_DATABASE_URI'],
                         c.DEFAULT_DATABASE)
        self.assertEqual(app.config['FREEZER_IGNORE_MIMETYPE_WARNINGS'],
                         c.DEFAULT_ARA_IGNORE_MIMETYPE_WARNINGS)

        self.assertEqual(app.config['ARA_ENABLE_DEBUG_VIEW'], False)
        self.assertEqual(app.config['ARA_AUTOCREATE_DATABASE'], True)
Example #16
0
def application(environ, start_response):
    # Apache SetEnv variables are passed only in environ variable
    if (int(environ.get('ARA_WSGI_USE_VIRTUALENV', 0)) == 1 and
       environ.get('ARA_WSGI_VIRTUALENV_PATH')):
        # Backwards compatibility, we did not always suffix activate_this.py
        activate_this = environ.get('ARA_WSGI_VIRTUALENV_PATH')
        if 'activate_this.py' not in activate_this:
            activate_this = os.path.join(activate_this, 'bin/activate_this.py')

        if six.PY2:
            execfile(activate_this, dict(__file__=activate_this))  # nosec
        else:
            exec(open(activate_this).read())  # nosec

    TMPDIR_MAX_AGE = int(environ.get('ARA_WSGI_TMPDIR_MAX_AGE', 3600))
    LOG_ROOT = environ.get('ARA_WSGI_LOG_ROOT', '/srv/static/logs')
    DATABASE_DIRECTORY = environ.get(
        'ARA_WSGI_DATABASE_DIRECTORY',
        'ara-report'
    )

    request = environ['REQUEST_URI']
    match = re.search('/(?P<path>.*/{}/)'.format(DATABASE_DIRECTORY), request)
    if not match:
        return bad_request(environ, start_response,
                           'No "/{}/" in URL.'.format(DATABASE_DIRECTORY))

    path = os.path.abspath(os.path.join(LOG_ROOT, match.group('path')))

    # Ensure we don't escape outside LOG_ROOT and we are looking at a
    # valid directory
    if not path.startswith(LOG_ROOT) or not os.path.isdir(path):
        logger.error('Directory access violation: %s' % path)
        return bad_request(environ, start_response, 'No directory found.')

    database = os.path.join(path, 'ansible.sqlite')
    if not os.path.isfile(database):
        return bad_request(environ, start_response, 'No ARA database found.')

    # ARA and Ansible (when loading configuration) both expect a directory
    # they are able to write to, this can be safely discarded.
    # Nothing is read from here and there is therefore no security risks.
    # It needs to be at a known location in order to be able to clean it up
    # so it doesn't accumulate needless directories and files.
    # TODO: ARA 1.0 no longer requires temporary directories, clean this up.
    tmpdir = '/tmp/ara_wsgi_sqlite'  # nosec
    if os.path.exists(tmpdir):
        # Periodically delete this directory to avoid accumulating directories
        # and files endlessly
        now = time.time()
        if now - TMPDIR_MAX_AGE > os.path.getmtime(tmpdir):
            shutil.rmtree(tmpdir, ignore_errors=True)
    os.environ['ANSIBLE_LOCAL_TEMP'] = os.path.join(tmpdir, '.ansible')
    os.environ['ARA_DIR'] = os.path.join(tmpdir, '.ara')

    # Path to the ARA database
    os.environ['ARA_DATABASE'] = 'sqlite:///{}'.format(database)
    # The intent is that we are dealing with databases that already exist.
    # Therefore, we're not really interested in creating the database and
    # making sure that the SQL migrations are done. Toggle that off.
    # This needs to be a string, we're setting an environment variable
    os.environ['ARA_AUTOCREATE_DATABASE'] = 'false'

    msg = 'Request {request} mapped to {database} with root {root}'.format(
        request=request,
        database='sqlite:///{}'.format(database),
        root=match.group('path')
    )
    logger.debug(msg)

    from ara.webapp import create_app
    try:
        app = create_app()
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///{}'.format(database)
        app.config['APPLICATION_ROOT'] = match.group('path')
        return app(environ, start_response)
    except Exception as e:
        # We're staying relatively vague on purpose to avoid disclosure
        logger.error('ARA bootstrap failure for %s: %s' % (database, str(e)))
        return bad_request(environ, start_response, 'ARA bootstrap failure.')
Example #17
0
 def create_app(self):
     return w.create_app(self)
Example #18
0
    def run(self, tmp=None, task_vars=None):
        if task_vars is None:
            task_vars = dict()

        if not HAS_ARA:
            result = {
                'failed': True,
                'msg': 'ARA is required to run this module.'
            }
            return result

        for arg in self._task.args:
            if arg not in self.VALID_ARGS:
                result = {
                    "failed": True,
                    "msg": '{0} is not a valid option.'.format(arg)
                }
                return result

        result = super(ActionModule, self).run(tmp, task_vars)

        playbook_id = self._task.args.get('playbook', None)
        key = self._task.args.get('key', None)
        value = self._task.args.get('value', None)
        type = self._task.args.get('type', 'text')

        required = ['key', 'value']
        for parameter in required:
            if not self._task.args.get(parameter):
                result['failed'] = True
                result['msg'] = "Parameter '{0}' is required".format(parameter)
                return result

        if type not in self.VALID_TYPES:
            result['failed'] = True
            msg = "Type '{0}' is not supported, choose one of: {1}".format(
                type, ", ".join(self.VALID_TYPES)
            )
            result['msg'] = msg
            return result

        app = create_app()
        if not current_app:
            context = app.app_context()
            context.push()

        if playbook_id is None:
            # Retrieve playbook_id from the cached context
            playbook_id = current_app._cache['playbook']

        try:
            self.create_or_update_key(playbook_id, key, value, type)
            result['key'] = key
            result['value'] = value
            result['type'] = type
            result['playbook_id'] = playbook_id
            result['msg'] = 'Data recorded in ARA for this playbook.'
        except Exception as e:
            result['failed'] = True
            result['msg'] = 'Data not recorded in ARA: {0}'.format(str(e))
        return result
Example #19
0
 def create_app(self):
     return w.create_app(self)
Example #20
0
    def run(self, tmp=None, task_vars=None):
        if task_vars is None:
            task_vars = dict()

        if not HAS_ARA:
            result = {
                'failed': True,
                'msg': 'ARA is required to run this module.'
            }
            return result

        app = create_app()
        if not current_app:
            context = app.app_context()
            context.push()

        for arg in self._task.args:
            if arg not in self.VALID_ARGS:
                result = {
                    "failed": True,
                    "msg": '{0} is not a valid option.'.format(arg)
                }
                return result

        result = super(ActionModule, self).run(tmp, task_vars)

        playbook_id = self._task.args.get('playbook', None)
        key = self._task.args.get('key', None)
        value = self._task.args.get('value', None)
        type = self._task.args.get('type', 'text')

        required = ['key', 'value']
        for parameter in required:
            if not self._task.args.get(parameter):
                result['failed'] = True
                result['msg'] = "Parameter '{0}' is required".format(parameter)
                return result

        if type not in self.VALID_TYPES:
            result['failed'] = True
            msg = "Type '{0}' is not supported, choose one of: {1}".format(
                type, ", ".join(self.VALID_TYPES)
            )
            result['msg'] = msg
            return result

        if playbook_id is None:
            # Retrieve the persisted playbook_id from tmpfile
            tmpfile = os.path.join(app.config['ARA_TMP_DIR'], 'ara.json')
            with open(tmpfile, 'rb') as file:
                data = jsonutils.load(file)
            playbook_id = data['playbook']['id']

        try:
            self.create_or_update_key(playbook_id, key, value, type)
            result['key'] = key
            result['value'] = value
            result['type'] = type
            result['playbook_id'] = playbook_id
            result['msg'] = 'Data recorded in ARA for this playbook.'
        except Exception as e:
            result['failed'] = True
            result['msg'] = 'Data not recorded in ARA: {0}'.format(str(e))
        return result
Example #21
0
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#   License for the specific language governing permissions and limitations
#   under the License.

import json
import os

from ansible.plugins.action import ActionBase

try:
    from ara import models
    from ara.models import db
    from ara.webapp import create_app
    app = create_app()
    HAS_ARA = True
except ImportError:
    HAS_ARA = False

DOCUMENTATION = '''
---
module: ara_record
short_description: Ansible module to record persistent data with ARA.
version_added: "2.0"
author: "RDO Community <*****@*****.**>"
description:
    - Ansible module to record persistent data with ARA.
options:
    key:
        description:
Example #22
0
def application(environ, start_response):
    os.environ['ANSIBLE_CONFIG'] = environ['ANSIBLE_CONFIG']
    from ara.webapp import create_app
    app = create_app()
    return app(environ, start_response)
Example #23
0
#   under the License.

from collections import defaultdict
import random
import os
import json

from ara.webapp import create_app
import ara.models as m
import ara.utils as u
import ara.plugins.callbacks.log_ara as l

from ara.tests.unit.common import TestAra
from ara.tests.unit import fakes

a = create_app()


class TestCallback(TestAra):
    """ Tests for the Ansible callback module """
    def setUp(self):
        super(TestCallback, self).setUp()

        self.cb = l.CallbackModule()
        self.tag = '%04d' % random.randint(0, 9999)

        self.ansible_run()

    def tearDown(self):
        super(TestCallback, self).tearDown()
def application(environ, start_response):
    # Environment variables passed by mod_wsgi land in environ while environment
    # variables passed through gunicorn land in os.environ.
    config_variables = [
        "ARA_WSGI_USE_VIRTUALENV",
        "ARA_WSGI_VIRTUALENV_PATH",
        "ARA_WSGI_TMPDIR_MAX_AGE",
        "ARA_WSGI_LOG_ROOT",
        "ARA_WSGI_DATABASE_DIRECTORY"
    ]
    for variable in config_variables:
        if variable in os.environ:
            environ[variable] = os.environ[variable]

    if (int(environ.get('ARA_WSGI_USE_VIRTUALENV', 0)) == 1 and
       environ.get('ARA_WSGI_VIRTUALENV_PATH')):
        # Backwards compatibility, we did not always suffix activate_this.py
        activate_this = environ.get('ARA_WSGI_VIRTUALENV_PATH')
        if 'activate_this.py' not in activate_this:
            activate_this = os.path.join(activate_this, 'bin/activate_this.py')

        if six.PY2:
            execfile(activate_this, dict(__file__=activate_this))  # nosec
        else:
            exec(open(activate_this).read())  # nosec

    TMPDIR_MAX_AGE = int(environ.get('ARA_WSGI_TMPDIR_MAX_AGE', 3600))
    LOG_ROOT = environ.get('ARA_WSGI_LOG_ROOT', '/srv/static/logs')
    DATABASE_DIRECTORY = environ.get(
        'ARA_WSGI_DATABASE_DIRECTORY',
        'ara-report'
    )

    # mod_wsgi uses REQUEST_URI, standardize on PATH_INFO instead
    if 'REQUEST_URI' in environ:
        environ['PATH_INFO'] = environ['SCRIPT_URL']

    request = environ['PATH_INFO']
    match = re.search('/(?P<path>.*/{}/)'.format(DATABASE_DIRECTORY), request)
    if not match:
        return bad_request(environ, start_response,
                           'No "/{}/" in URL.'.format(DATABASE_DIRECTORY))

    # It is not sufficient to set Flask's APPLICATION_ROOT, we must also set
    # the SCRIPT_NAME variable to define the directory out of which the
    # application should be served.
    # See issues:
    #   - https://github.com/pallets/flask/issues/1714
    #   - https://github.com/pallets/flask/issues/2759
    if not environ['SCRIPT_NAME']:
        environ['SCRIPT_NAME'] = "/%s" % match.group('path')

    # When SCRIPT_NAME is set, remove it from PATH_INFO
    if environ['PATH_INFO'].startswith(environ['SCRIPT_NAME']):
        environ['PATH_INFO'] = environ['PATH_INFO'][len(environ['SCRIPT_NAME']):]
        if not environ['PATH_INFO']:
            environ['PATH_INFO'] = '/'

    path = os.path.abspath(os.path.join(LOG_ROOT, match.group('path')))

    # Ensure we don't escape outside LOG_ROOT and we are looking at a
    # valid directory
    if not path.startswith(LOG_ROOT) or not os.path.isdir(path):
        logger.error('Directory access violation: %s' % path)
        return bad_request(environ, start_response, 'No directory found.')

    database = os.path.join(path, 'ansible.sqlite')
    if not os.path.isfile(database):
        return bad_request(environ, start_response, 'No ARA database found.')

    # ARA and Ansible (when loading configuration) both expect a directory
    # they are able to write to, this can be safely discarded.
    # Nothing is read from here and there is therefore no security risks.
    # It needs to be at a known location in order to be able to clean it up
    # so it doesn't accumulate needless directories and files.
    # TODO: ARA 1.0 no longer requires temporary directories, clean this up.
    tmpdir = '/tmp/ara_wsgi_sqlite'  # nosec
    if os.path.exists(tmpdir):
        # Periodically delete this directory to avoid accumulating directories
        # and files endlessly
        now = time.time()
        if now - TMPDIR_MAX_AGE > os.path.getmtime(tmpdir):
            shutil.rmtree(tmpdir, ignore_errors=True)
    os.environ['ANSIBLE_LOCAL_TEMP'] = os.path.join(tmpdir, '.ansible')
    os.environ['ARA_DIR'] = os.path.join(tmpdir, '.ara')

    # The intent is that we are dealing with databases that already exist.
    # Therefore, we're not really interested in creating the database and
    # making sure that the SQL migrations are done. Toggle that off.
    # This needs to be a string, we're setting an environment variable
    os.environ['ARA_AUTOCREATE_DATABASE'] = 'false'

    msg = 'Request {request} mapped to {database} with root {root}'.format(
        request=request,
        database='sqlite:///{}'.format(database),
        root=match.group('path')
    )
    logger.debug(msg)

    from ara.webapp import create_app
    try:
        app = create_app()
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///{}'.format(database)
        app.config['APPLICATION_ROOT'] = environ['PATH_INFO']
        return app(environ, start_response)
    except Exception as e:
        # We're staying relatively vague on purpose to avoid disclosure
        logger.error('ARA bootstrap failure for %s: %s' % (database, str(e)))
        return bad_request(environ, start_response, 'ARA bootstrap failure.')
Example #25
0
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with ARA.  If not, see <http://www.gnu.org/licenses/>.


from flask_script import commands
from flask_script import Manager
from flask_script import prompt_bool
from flask_migrate import Migrate
from flask_migrate import MigrateCommand

from ara.webapp import create_app
from ara.models import db

app = create_app()
manager = Manager(app)
manager.add_command('db', MigrateCommand)
migrate = Migrate(app, db, directory=app.config['DB_MIGRATIONS'])

# Overwrite the default runserver command to be able to pass a custom host
# and port from the config as the defaults
manager.add_command(
    "runserver",
    commands.Server(host=app.config['ARA_HOST'],
                    port=app.config['ARA_PORT'],
                    processes=8,
                    threaded=False)
)