Exemple #1
0
    def build_approval_notification_message(self, nt, approval_status):
        env = sandbox.ImmutableSandboxedEnvironment()

        context = self.context(approval_status)

        msg_template = body_template = None
        msg = body = ''

        # Use custom template if available
        if nt.messages and nt.messages.get('workflow_approval', None):
            template = nt.messages['workflow_approval'].get(
                approval_status, {})
            msg_template = template.get('message', None)
            body_template = template.get('body', None)
        # If custom template not provided, look up default template
        default_template = nt.notification_class.default_messages[
            'workflow_approval'][approval_status]
        if not msg_template:
            msg_template = default_template.get('message', None)
        if not body_template:
            body_template = default_template.get('body', None)

        if msg_template:
            try:
                msg = env.from_string(msg_template).render(**context)
            except (TemplateSyntaxError, UndefinedError, SecurityError):
                msg = ''

        if body_template:
            try:
                body = env.from_string(body_template).render(**context)
            except (TemplateSyntaxError, UndefinedError, SecurityError):
                body = ''

        return (msg, body)
Exemple #2
0
    def build_notification_message(self, nt, status):
        env = sandbox.ImmutableSandboxedEnvironment()

        from awx.api.serializers import UnifiedJobSerializer
        job_serialization = UnifiedJobSerializer(self).to_representation(self)
        context = self.context(job_serialization)

        msg_template = body_template = None
        msg = body = ''

        # Use custom template if available
        if nt.messages:
            template = nt.messages.get(self.STATUS_TO_TEMPLATE_TYPE[status], {}) or {}
            msg_template = template.get('message', None)
            body_template = template.get('body', None)
        # If custom template not provided, look up default template
        default_template = nt.notification_class.default_messages[self.STATUS_TO_TEMPLATE_TYPE[status]]
        if not msg_template:
            msg_template = default_template.get('message', None)
        if not body_template:
            body_template = default_template.get('body', None)

        if msg_template:
            try:
                msg = env.from_string(msg_template).render(**context)
            except (TemplateSyntaxError, UndefinedError, SecurityError):
                msg = ''

        if body_template:
            try:
                body = env.from_string(body_template).render(**context)
            except (TemplateSyntaxError, UndefinedError, SecurityError):
                body = ''

        return (msg, body)
Exemple #3
0
    def build_notification_message(self, nt, status):
        env = sandbox.ImmutableSandboxedEnvironment()

        from awx.api.serializers import UnifiedJobSerializer
        job_serialization = UnifiedJobSerializer(self).to_representation(self)
        context = self.context(job_serialization)

        msg_template = body_template = None

        if nt.messages:
            templates = nt.messages.get(self.STATUS_TO_TEMPLATE_TYPE[status], {}) or {}
            msg_template = templates.get('message', {})
            body_template = templates.get('body', {})

        if msg_template:
            try:
                notification_subject = env.from_string(msg_template).render(**context)
            except (TemplateSyntaxError, UndefinedError, SecurityError):
                notification_subject = ''
        else:
            notification_subject = u"{} #{} '{}' {}: {}".format(self.get_notification_friendly_name(),
                                                                self.id,
                                                                self.name,
                                                                status,
                                                                self.get_ui_url())
        notification_body = self.notification_data()
        notification_body['friendly_name'] = self.get_notification_friendly_name()
        if body_template:
            try:
                notification_body['body'] = env.from_string(body_template).render(**context)
            except (TemplateSyntaxError, UndefinedError, SecurityError):
                notification_body['body'] = ''

        return (notification_subject, notification_body)
Exemple #4
0
def get_jinja_environment(template, extra_globals=None):
    """Return a sandboxed jinja environment."""
    template_map = {'template': template}
    env = sandbox.ImmutableSandboxedEnvironment(
        loader=jinja2.DictLoader(template_map), bytecode_cache=CompilerCache())
    env.filters['prepend'] = do_prepend
    env.filters['preserve'] = preserve_linefeeds
    env.globals['json'] = json
    if extra_globals:
        env.globals.update(extra_globals)
    return env
Exemple #5
0
    def build_notification_message(self, event_type, context):
        env = sandbox.ImmutableSandboxedEnvironment()
        templates = self.get_message(event_type)
        msg_template = templates.get('message', {})

        try:
            notification_subject = env.from_string(msg_template).render(
                **context)
        except (TemplateSyntaxError, UndefinedError, SecurityError):
            notification_subject = ''

        msg_body = templates.get('body', {})
        try:
            notification_body = env.from_string(msg_body).render(**context)
        except (TemplateSyntaxError, UndefinedError, SecurityError):
            notification_body = ''

        return (notification_subject, notification_body)
Exemple #6
0
    def inject_credential(self, credential, env, safe_env, args, private_data_dir):
        """
        Inject credential data into the environment variables and arguments
        passed to `ansible-playbook`

        :param credential:       a :class:`awx.main.models.Credential` instance
        :param env:              a dictionary of environment variables used in
                                 the `ansible-playbook` call.  This method adds
                                 additional environment variables based on
                                 custom `env` injectors defined on this
                                 CredentialType.
        :param safe_env:         a dictionary of environment variables stored
                                 in the database for the job run
                                 (`UnifiedJob.job_env`); secret values should
                                 be stripped
        :param args:             a list of arguments passed to
                                 `ansible-playbook` in the style of
                                 `subprocess.call(args)`.  This method appends
                                 additional arguments based on custom
                                 `extra_vars` injectors defined on this
                                 CredentialType.
        :param private_data_dir: a temporary directory to store files generated
                                 by `file` injectors (like config files or key
                                 files)
        """
        if not self.injectors:
            if self.managed_by_tower and credential.credential_type.namespace in dir(builtin_injectors):
                injected_env = {}
                getattr(builtin_injectors, credential.credential_type.namespace)(credential, injected_env, private_data_dir)
                env.update(injected_env)
                safe_env.update(build_safe_env(injected_env))
            return

        class TowerNamespace:
            pass

        tower_namespace = TowerNamespace()

        # maintain a normal namespace for building the ansible-playbook arguments (env and args)
        namespace = {'tower': tower_namespace}

        # maintain a sanitized namespace for building the DB-stored arguments (safe_env)
        safe_namespace = {'tower': tower_namespace}

        # build a normal namespace with secret values decrypted (for
        # ansible-playbook) and a safe namespace with secret values hidden (for
        # DB storage)
        injectable_fields = list(credential.inputs.keys()) + credential.dynamic_input_fields
        for field_name in list(set(injectable_fields)):
            value = credential.get_input(field_name)

            if type(value) is bool:
                # boolean values can't be secret/encrypted/external
                safe_namespace[field_name] = namespace[field_name] = value
                continue

            if field_name in self.secret_fields:
                safe_namespace[field_name] = '**********'
            elif len(value):
                safe_namespace[field_name] = value
            if len(value):
                namespace[field_name] = value

        for field in self.inputs.get('fields', []):
            # default missing boolean fields to False
            if field['type'] == 'boolean' and field['id'] not in credential.inputs.keys():
                namespace[field['id']] = safe_namespace[field['id']] = False
            # make sure private keys end with a \n
            if field.get('format') == 'ssh_private_key':
                if field['id'] in namespace and not namespace[field['id']].endswith('\n'):
                    namespace[field['id']] += '\n'

        file_tmpls = self.injectors.get('file', {})
        # If any file templates are provided, render the files and update the
        # special `tower` template namespace so the filename can be
        # referenced in other injectors

        sandbox_env = sandbox.ImmutableSandboxedEnvironment()

        for file_label, file_tmpl in file_tmpls.items():
            data = sandbox_env.from_string(file_tmpl).render(**namespace)
            _, path = tempfile.mkstemp(dir=private_data_dir)
            with open(path, 'w') as f:
                f.write(data)
            os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
            # FIXME: develop some better means of referencing paths inside containers
            container_path = os.path.join('/runner', os.path.basename(path))

            # determine if filename indicates single file or many
            if file_label.find('.') == -1:
                tower_namespace.filename = container_path
            else:
                if not hasattr(tower_namespace, 'filename'):
                    tower_namespace.filename = TowerNamespace()
                file_label = file_label.split('.')[1]
                setattr(tower_namespace.filename, file_label, container_path)

        injector_field = self._meta.get_field('injectors')
        for env_var, tmpl in self.injectors.get('env', {}).items():
            try:
                injector_field.validate_env_var_allowed(env_var)
            except ValidationError as e:
                logger.error('Ignoring prohibited env var {}, reason: {}'.format(env_var, e))
                continue
            env[env_var] = sandbox_env.from_string(tmpl).render(**namespace)
            safe_env[env_var] = sandbox_env.from_string(tmpl).render(**safe_namespace)

        if 'INVENTORY_UPDATE_ID' not in env:
            # awx-manage inventory_update does not support extra_vars via -e
            extra_vars = {}
            for var_name, tmpl in self.injectors.get('extra_vars', {}).items():
                extra_vars[var_name] = sandbox_env.from_string(tmpl).render(**namespace)

            def build_extra_vars_file(vars, private_dir):
                handle, path = tempfile.mkstemp(dir=private_dir)
                f = os.fdopen(handle, 'w')
                f.write(safe_dump(vars))
                f.close()
                os.chmod(path, stat.S_IRUSR)
                return path

            if extra_vars:
                path = build_extra_vars_file(extra_vars, private_data_dir)
                # FIXME: develop some better means of referencing paths inside containers
                container_path = os.path.join('/runner', os.path.basename(path))
                args.extend(['-e', '@%s' % container_path])
Exemple #7
0
    def validate(self, value, model_instance):
        super(CredentialTypeInjectorField,
              self).validate(value, model_instance)

        # make sure the inputs are valid first
        try:
            CredentialTypeInputField().validate(model_instance.inputs,
                                                model_instance)
        except django_exceptions.ValidationError:
            # If `model_instance.inputs` itself is invalid, we can't make an
            # estimation as to whether our Jinja templates contain valid field
            # names; don't continue
            return

        # In addition to basic schema validation, search the injector fields
        # for template variables and make sure they match the fields defined in
        # the inputs
        valid_namespace = dict(
            (field, 'EXAMPLE') for field in model_instance.defined_fields)

        class ExplodingNamespace:
            def __str__(self):
                raise UndefinedError(
                    _('Must define unnamed file injector in order to reference `tower.filename`.'
                      ))

        class TowerNamespace:
            def __init__(self):
                self.filename = ExplodingNamespace()

            def __str__(self):
                raise UndefinedError(
                    _('Cannot directly reference reserved `tower` namespace container.'
                      ))

        valid_namespace['tower'] = TowerNamespace()

        # ensure either single file or multi-file syntax is used (but not both)
        template_names = [
            x for x in value.get('file', {}).keys() if x.startswith('template')
        ]
        if 'template' in template_names:
            valid_namespace['tower'].filename = 'EXAMPLE_FILENAME'
            if len(template_names) > 1:
                raise django_exceptions.ValidationError(
                    _('Must use multi-file syntax when injecting multiple files'
                      ),
                    code='invalid',
                    params={'value': value},
                )
        elif template_names:
            for template_name in template_names:
                template_name = template_name.split('.')[1]
                setattr(valid_namespace['tower'].filename, template_name,
                        'EXAMPLE_FILENAME')

        for type_, injector in value.items():
            if type_ == 'env':
                for key in injector.keys():
                    self.validate_env_var_allowed(key)
            for key, tmpl in injector.items():
                try:
                    sandbox.ImmutableSandboxedEnvironment(
                        undefined=StrictUndefined).from_string(tmpl).render(
                            valid_namespace)
                except UndefinedError as e:
                    raise django_exceptions.ValidationError(
                        _('{sub_key} uses an undefined field ({error_msg})').
                        format(sub_key=key, error_msg=e),
                        code='invalid',
                        params={'value': value},
                    )
                except SecurityError as e:
                    raise django_exceptions.ValidationError(
                        _('Encountered unsafe code execution: {}').format(e))
                except TemplateSyntaxError as e:
                    raise django_exceptions.ValidationError(
                        _('Syntax error rendering template for {sub_key} inside of {type} ({error_msg})'
                          ).format(sub_key=key, type=type_, error_msg=e),
                        code='invalid',
                        params={'value': value},
                    )
Exemple #8
0
    process_access_request,
)
from grandchallenge.evaluation.utils import get
from grandchallenge.hanging_protocols.models import ViewContentMixin
from grandchallenge.modalities.models import ImagingModality
from grandchallenge.organizations.models import Organization
from grandchallenge.publications.models import Publication
from grandchallenge.subdomains.utils import reverse
from grandchallenge.workstations.models import Workstation

logger = logging.getLogger(__name__)

DEFAULT_INPUT_INTERFACE_SLUG = "generic-medical-image"
DEFAULT_OUTPUT_INTERFACE_SLUG = "generic-overlay"

JINJA_ENGINE = sandbox.ImmutableSandboxedEnvironment()


class Algorithm(UUIDModel, TitleSlugDescriptionModel, ViewContentMixin):
    editors_group = models.OneToOneField(
        Group,
        on_delete=models.PROTECT,
        editable=False,
        related_name="editors_of_algorithm",
    )
    users_group = models.OneToOneField(
        Group,
        on_delete=models.PROTECT,
        editable=False,
        related_name="users_of_algorithm",
    )