Example #1
0
 def new_from_migration(self, actor_type, state, prev_connections=None, callback=None):
     """Instantiate an actor of type 'actor_type' and apply the 'state' to the actor."""
     try:
         _log.analyze(self.node.id, "+", state)
         subject_attributes = state.pop('subject_attributes', None)
         migration_info = state.pop('migration_info', None)
         try:
             state['_managed'].remove('subject_attributes')
             state['_managed'].remove('migration_info')
         except:
             pass
         if security_enabled():
             security = Security(self.node)
             security.set_subject_attributes(subject_attributes)
         else:
             security = None
         actor_def, signer = self.lookup_and_verify(actor_type, security)
         requirements = actor_def.requires if hasattr(actor_def, "requires") else []
         self.check_requirements_and_sec_policy(requirements, security, state['id'],
                                                signer, migration_info,
                                                CalvinCB(self.new, actor_type, None,
                                                         state, prev_connections,
                                                         callback=callback,
                                                         actor_def=actor_def,
                                                         security=security))
     except Exception:
         # Still want to create shadow actor.
         self.new(actor_type, None, state, prev_connections, callback=callback, shadow_actor=True)
Example #2
0
 def _new_actor(self,
                actor_type,
                class_=None,
                actor_id=None,
                security=None,
                access_decision=None,
                shadow_actor=False):
     """Return a 'bare' actor of actor_type, raises an exception on failure."""
     if security_enabled() and not access_decision:
         _log.debug(
             "Security policy check for actor failed, access_decision={}".
             format(access_decision))
         shadow_actor = True
     if shadow_actor:
         class_ = ShadowActor
     if class_ is None:
         try:
             class_, signer = self.lookup_and_verify(actor_type, security)
         except Exception:
             class_ = ShadowActor
     try:
         # Create a 'bare' instance of the actor
         a = class_(actor_type, actor_id=actor_id, security=security)
     except Exception as e:
         _log.error("The actor %s(%s) can't be instantiated." %
                    (actor_type, class_.__init__))
         raise (e)
     if isinstance(access_decision, tuple):
         # Authorization checks needed if access_decision is a tuple.
         a.set_authorization_checks(access_decision[1])
     return a
Example #3
0
 def _new_actor(self, actor_type, class_=None, actor_id=None, security=None, access_decision=None, shadow_actor=False):
     """Return a 'bare' actor of actor_type, raises an exception on failure."""
     if security_enabled() and not access_decision:
         try:
             _log.debug("Security policy check for actor failed")
             raise Exception("Security policy check for actor failed")
         except:
             shadow_actor = True
     if shadow_actor:
         class_ = ShadowActor
     if class_ is None:
         try:
             class_, signer = self.lookup_and_verify(actor_type, security)
         except Exception:
             class_ = ShadowActor
     try:
         # Create a 'bare' instance of the actor
         a = class_(actor_type, actor_id=actor_id, security=security)
     except Exception as e:
         _log.error("The actor %s(%s) can't be instantiated." % (actor_type, class_.__init__))
         raise(e)
     a._calvinsys = self.node.calvinsys()
     if isinstance(access_decision, tuple):
         # Authorization checks needed if access_decision is a tuple.
         a.set_authorization_checks(access_decision[1])
     return a
Example #4
0
 def add_node(self, node, cb=None):
     """
     Add node to storage
     """
     self.set(prefix="node-", key=node.id,
               value={"uri": node.external_uri,
                      "control_uri": node.external_control_uri,
                      "authz_server": node.authorization.authz_server_id,
                      "attributes": {'public': node.attributes.get_public(),
                                     'indexed_public': node.attributes.get_indexed_public(as_list=False)}}, cb=cb)
     self._add_node_index(node)
     # Store all actors on this node in storage
     GlobalStore(node=node, security=Security(node) if security_enabled() else None, verify=False).export()
Example #5
0
 def check_requirements_and_sec_policy(self, requirements, security=None, actor_id=None,
                                       signer=None, decision_from_migration=None, callback=None):
     """Check requirements and security policy for actor."""
     # Check if node has the capabilities required by the actor.
     for req in requirements:
         if not self.node.calvinsys().has_capability(req):
             raise Exception("Actor requires %s" % req)
     if security_enabled():
         # Check if access is permitted for the actor by the security policy.
         # Will continue directly with callback if authorization is not enabled.
         security.check_security_policy(callback, "actor", actor_id, ['runtime'] + requirements,
                                        signer, decision_from_migration)
     else:
         callback()
 def check_requirements_and_sec_policy(self, requirements, security=None, actor_id=None,
                                       signer=None, decision_from_migration=None, callback=None):
     for req in requirements:
         if not get_calvinsys().has_capability(req) and not get_calvinlib().has_capability(req):
             raise Exception("Actor requires %s" % req)
     if security_enabled():
         # Check if access is permitted for the actor by the security policy.
         # Will continue directly with callback if authorization is not enabled.
         security.check_security_policy(callback, actor_id=actor_id, requires=['runtime'] + requirements,
                                         element_type="actor",
                                         element_value=signer,
                                         decision_from_migration=decision_from_migration)
     else:
         callback()
    def wrapper(self, handle, connection, match, data, hdr):

        def _unauthorized_response():
            self.send_response(handle, connection, None, status=calvinresponse.UNAUTHORIZED)

        def _handle_authentication_decision(authentication_decision):
            _log.debug("control_apis.authentication::_handle_authentication_decision, authentication_decision={}".format(authentication_decision))
            if not authentication_decision:
                _unauthorized_response()
                return
            try:
                self.security.check_security_policy(
                    CalvinCB(_handle_policy_decision),
                    element_type="control_interface",
                    element_value=arguments['func'].__name__
                )
            except Exception as exc:
                _log.exception("Failed to check security policy, exc={}".format(exc))
                _unauthorized_response()

        def _handle_policy_decision(access_decision):
            if not access_decision:
                _unauthorized_response()
                return
            # Yay! We made it!
            func(self, handle, connection, match, data, hdr)

        #
        # Exit early if security disabled
        #
        if not security_enabled():
            func(self, handle, connection, match, data, hdr)
            return
        #
        # Verify the request against credentials and policy
        #
        credentials = None
        arguments={'func':func, 'self':self, 'handle':handle, 'connection':connection, 'match':match, 'data':data, 'hdr':hdr}
        try:
            if 'authorization' in hdr:
                cred = b64decode(hdr['authorization'].strip('Basic ')).split(':')
                credentials ={'user':cred[0], 'password':cred[1]}
        except TypeError as err:
            _log.error("_verify_permission: Missing or wrongly formatted credentials in request header")
            # Continue without credentials, policy might allow access
        try:
            self.security.authenticate_subject(credentials, callback=CalvinCB(_handle_authentication_decision))
        except Exception as exc:
            _log.exception("Failed to authenticate the subject, exc={}".format(exc))
            _unauthorized_response()
Example #8
0
 def new_from_migration(self,
                        actor_type,
                        state,
                        prev_connections=None,
                        connection_list=None,
                        callback=None):
     """Instantiate an actor of type 'actor_type' and apply the 'state' to the actor."""
     try:
         _log.analyze(self.node.id, "+", state)
         subject_attributes = state['security'].pop('_subject_attributes',
                                                    None)
         migration_info = state['private'].pop('_migration_info', None)
         try:
             state['security'].remove('_subject_attributes')
             state['private'].remove('_migration_info')
         except:
             pass
         if security_enabled():
             security = Security(self.node)
             security.set_subject_attributes(subject_attributes)
         else:
             security = None
         actor_def, signer = self.lookup_and_verify(actor_type, security)
         requirements = actor_def.requires if hasattr(
             actor_def, "requires") else []
         self.check_requirements_and_sec_policy(
             requirements, security, state['private']['_id'], signer,
             migration_info,
             CalvinCB(self.new,
                      actor_type,
                      None,
                      state,
                      prev_connections=prev_connections,
                      connection_list=connection_list,
                      callback=callback,
                      actor_def=actor_def,
                      security=security))
     except Exception:
         # Still want to create shadow actor.
         self.new(actor_type,
                  None,
                  state,
                  prev_connections=prev_connections,
                  connection_list=connection_list,
                  callback=callback,
                  shadow_actor=True)
Example #9
0
 def check_requirements_and_sec_policy(self,
                                       requirements,
                                       security=None,
                                       actor_id=None,
                                       signer=None,
                                       decision_from_migration=None,
                                       callback=None):
     for req in requirements:
         if not get_calvinsys().has_capability(
                 req) and not get_calvinlib().has_capability(req):
             raise Exception("Actor requires %s" % req)
     if security_enabled():
         # Check if access is permitted for the actor by the security policy.
         # Will continue directly with callback if authorization is not enabled.
         security.check_security_policy(
             callback,
             actor_id=actor_id,
             requires=['runtime'] + requirements,
             element_type="actor",
             element_value=signer,
             decision_from_migration=decision_from_migration)
     else:
         callback()
Example #10
0
def compile_script_check_security(data,
                                  filename,
                                  cb,
                                  security=None,
                                  content=None,
                                  verify=True,
                                  node=None,
                                  signature=None):
    """
    Compile a script and return a tuple (deployable, errors, warnings).

    'credentials' are optional security credentials(?)
    'verify' is deprecated and will be removed
    'node' is the runtime performing security check(?)
    'cb' is a CalvinCB callback

    N.B. If callback 'cb' is given, this method calls cb(deployable, errors, warnings) and returns None
    N.B. If callback 'cb' is given, and method runs to completion, cb is called with additional parameter 'security' (?)
    """
    def _exit_with_error(callback):
        """Helper method to generate a proper error"""
        it = IssueTracker()
        it.add_error("UNAUTHORIZED", info={'status': 401})
        callback({}, it)
        return

    def _handle_policy_decision(data,
                                appname,
                                verify,
                                access_decision,
                                org_cb,
                                security=None):
        if not access_decision:
            _log.error("Access denied")
            # This error reason is detected in calvin control and gives proper REST response
            _exit_with_error(org_cb)
            return
        if 'app_info' not in data and 'script' in data:
            deployable, issuetracker = compile_script(data['script'], appname)
        elif 'app_info' in data:
            deployable = data['app_info']
            issuetracker = IssueTracker()
        else:
            _log.error("Neither app_info or script supplied")
            # This error reason is detected in calvin control and gives proper REST response
            _exit_with_error(org_cb)
            return
        org_cb(deployable, issuetracker, security=security)

    #
    # Actual code for compile_script
    #
    appname = appname_from_filename(filename)
    # FIXME: if node is None we bypass security even if enabled. Is that the intention?
    if node is not None and security_enabled():
        # FIXME: If cb is None, we will return from this method with None instead of a tuple, failing silently
        if security:
            sec = security
        else:
            sec = Security(node)

        verified, signer = sec.verify_signature_content(content, "application")
        if not verified:
            # Verification not OK if sign or cert not OK.
            _log.error("Failed application verification")
            # This error reason is detected in calvin control and gives proper REST response
            _exit_with_error(cb)
            return
        sec.check_security_policy(CalvinCB(_handle_policy_decision,
                                           data,
                                           appname,
                                           verify,
                                           security=security,
                                           org_cb=cb),
                                  element_type="application",
                                  element_value=signer)
        return

    #
    # We get here if node is None, or security is disabled
    #
    # This used to be
    # _handle_policy_decision(data, filename, verify, access_decision=True, security=None, org_cb=cb)
    # but since _handle_policy_decision is called with access_decision=True, security=None only compile_script would be called
    if 'app_info' not in data and 'script' in data:
        deployable, issuetracker = compile_script(data['script'], appname)
    elif 'app_info' in data:
        deployable = data['app_info']
        issuetracker = IssueTracker()
    else:
        _log.error("Neither app_info or script supplied")
        # This error reason is detected in calvin control and gives proper REST response
        _exit_with_error(org_cb)
        return
    cb(deployable, issuetracker, security=None)
Example #11
0
def compile_script_check_security(data, filename, cb, security=None, content=None, verify=True, node=None, signature=None):
    """
    Compile a script and return a tuple (deployable, errors, warnings).

    'credentials' are optional security credentials(?)
    'verify' is deprecated and will be removed
    'node' is the runtime performing security check(?)
    'cb' is a CalvinCB callback

    N.B. If callback 'cb' is given, this method calls cb(deployable, errors, warnings) and returns None
    N.B. If callback 'cb' is given, and method runs to completion, cb is called with additional parameter 'security' (?)
    """
    def _exit_with_error(callback):
        """Helper method to generate a proper error"""
        it = IssueTracker()
        it.add_error("UNAUTHORIZED", info={'status':401})
        callback({}, it)
        return

    def _handle_policy_decision(data, appname, verify, access_decision, org_cb, security=None):
        if not access_decision:
            _log.error("Access denied")
            # This error reason is detected in calvin control and gives proper REST response
            _exit_with_error(org_cb)
            return
        if 'app_info' not in data and 'script' in data:
            deployable, issuetracker = compile_script(data['script'], appname)
        elif 'app_info' in data:
            deployable = data['app_info']
            issuetracker = IssueTracker()
        else:
            _log.error("Neither app_info or script supplied")
            # This error reason is detected in calvin control and gives proper REST response
            _exit_with_error(org_cb)
            return
        org_cb(deployable, issuetracker, security=security)

    #
    # Actual code for compile_script
    #
    appname = appname_from_filename(filename)
    # FIXME: if node is None we bypass security even if enabled. Is that the intention?
    if node is not None and security_enabled():
        # FIXME: If cb is None, we will return from this method with None instead of a tuple, failing silently
        if security:
            sec = security
        else:
            sec = Security(node)


        verified, signer = sec.verify_signature_content(content, "application")
        if not verified:
            # Verification not OK if sign or cert not OK.
            _log.error("Failed application verification")
            # This error reason is detected in calvin control and gives proper REST response
            _exit_with_error(cb)
            return
        sec.check_security_policy(
            CalvinCB(_handle_policy_decision, data, appname, verify, security=security, org_cb=cb),
            element_type = "application",
            element_value = signer
        )
        return

    #
    # We get here if node is None, or security is disabled
    #
    # This used to be
    # _handle_policy_decision(data, filename, verify, access_decision=True, security=None, org_cb=cb)
    # but since _handle_policy_decision is called with access_decision=True, security=None only compile_script would be called
    if 'app_info' not in data and 'script' in data:
        deployable, issuetracker = compile_script(data['script'], appname)
    elif 'app_info' in data:
        deployable = data['app_info']
        issuetracker = IssueTracker()
    else:
        _log.error("Neither app_info or script supplied")
        # This error reason is detected in calvin control and gives proper REST response
        _exit_with_error(org_cb)
        return
    cb(deployable, issuetracker, security=None)
Example #12
0
def handle_deploy(self, handle, connection, match, data, hdr):
    """
    POST /deploy
    Compile and deploy a calvin script to this calvin node
    Apply deployment requirements to actors of an application
    and initiate migration of actors accordingly
    Body:
    {
        "name": <application name>,
        "script": <calvin script>  # alternativly "app_info"
        "app_info": <compiled script as app_info>  # alternativly "script"
        "sec_sign": {<cert hash>: <security signature of script>, ...} # optional and only with "script"
        "sec_credentials": <security credentials of user> # optional
        "deploy_info":
           {"groups": {"<group 1 name>": ["<actor instance 1 name>", ...]},  # TODO not yet implemented
            "requirements": {
                "<actor instance 1 name>": [ {"op": "<matching rule name>",
                                              "kwargs": {<rule param key>: <rule param value>, ...},
                                              "type": "+" or "-" for set intersection or set removal, respectively
                                              }, ...
                                           ],
                ...
                            }
           }
    }
    Note that either a script or app_info must be supplied. Optionally security
    verification of application script can be made. Also optionally user credentials
    can be supplied, some runtimes are configured to require credentials. The
    credentials takes for example the following form:
        {"user": <username>,
         "password": <password>,
         "role": <role>,
         "group": <group>,
         ...
        }

    The matching rules are implemented as plug-ins, intended to be extended.
    The type "+" is "and"-ing rules together (actually the intersection of all
    possible nodes returned by the rules.) The type "-" is explicitly removing
    the nodes returned by this rule from the set of possible nodes. Note that
    only negative rules will result in no possible nodes, i.e. there is no
    implied "all but these."

    A special matching rule exist, to first form a union between matching
    rules, i.e. alternative matches. This is useful for e.g. alternative
    namings, ownerships or specifying either of two specific nodes.
        {"op": "union_group",
         "requirements": [list as above of matching rules but without type key]
         "type": "+"
        }
    Other matching rules available is current_node, all_nodes and
    node_attr_match which takes an index param which is attribute formatted,
    e.g.
        {"op": "node_attr_match",
         "kwargs": {"index": ["node_name", {"organization": "org.testexample", "name": "testNode1"}]}
         "type": "+"
        }
    Response status code: OK, CREATED, BAD_REQUEST, UNAUTHORIZED or INTERNAL_ERROR
    Response: {"application_id": <application-id>,
               "actor_map": {<actor name with namespace>: <actor id>, ...}
               "placement": {<actor_id>: <node_id>, ...},
               "requirements_fulfilled": True/False}
    Failure response: {'errors': <compilation errors>,
                       'warnings': <compilation warnings>,
                       'exception': <exception string>}
    """
    try:
        _log.analyze(self.node.id, "+", data)
        if 'app_info' not in data:
            kwargs = {}
            credentials = ""
            # Supply security verification data when available
            content = None
            if "sec_credentials" in data:
                credentials = data['sec_credentials']
                content = {}
                if not "sec_sign" in data:
                    data['sec_sign'] = {}
                content = {
                    'file': data["script"],
                    'sign': {
                        h: s.decode('hex_codec')
                        for h, s in data['sec_sign'].iteritems()
                    }
                }
            compiler.compile_script_check_security(
                data["script"],
                filename=data["name"],
                security=self.security,
                content=content,
                node=self.node,
                verify=(data["check"] if "check" in data else True),
                cb=CalvinCB(self.handle_deploy_cont,
                            handle=handle,
                            connection=connection,
                            data=data),
                **kwargs)
        else:
            # Supplying app_info is for backward compatibility hence abort if node configured security
            # Main user is csruntime when deploying script at the same time and some tests used
            # via calvin.Tools.deployer (the Deployer below is the new in appmanager)
            # TODO rewrite these users to send the uncompiled script as cscontrol does.
            if security_enabled():
                _log.error(
                    "Can't combine compiled script with runtime having security"
                )
                self.send_response(handle,
                                   connection,
                                   None,
                                   status=calvinresponse.UNAUTHORIZED)
                return
            app_info = data['app_info']
            issuetracker = IssueTracker()
            self.handle_deploy_cont(app_info, issuetracker, handle, connection,
                                    data)
    except Exception as e:
        _log.exception("Deployer failed, e={}".format(e))
        self.send_response(handle,
                           connection,
                           json.dumps({'exception': str(e)}),
                           status=calvinresponse.INTERNAL_ERROR)
Example #13
0
def compile_script_check_security(source_text,
                                  filename,
                                  cb,
                                  credentials=None,
                                  verify=True,
                                  node=None):
    """
    Compile a script and return a tuple (deployable, errors, warnings).

    'credentials' are optional security credentials(?)
    'verify' is deprecated and will be removed
    'node' is the runtime performing security check(?)
    'cb' is a CalvinCB callback

    N.B. If callback 'cb' is given, this method calls cb(deployable, errors, warnings) and returns None
    N.B. If callback 'cb' is given, and method runs to completion, cb is called with additional parameter 'security' (?)
    """
    def _exit_with_error(callback):
        """Helper method to generate a proper error"""
        it = IssueTracker()
        it.add_error("UNAUTHORIZED", info={'status': 401})
        callback({}, it)

    def _handle_authentication_decision(source_text,
                                        appname,
                                        verify,
                                        authentication_decision,
                                        security,
                                        org_cb,
                                        content=None):
        if not authentication_decision:
            _log.error("Authentication failed")
            # This error reason is detected in calvin control and gives proper REST response
            _exit_with_error(org_cb)

        verified, signer = security.verify_signature_content(
            content, "application")
        if not verified:
            # Verification not OK if sign or cert not OK.
            _log.error("Failed application verification")
            # This error reason is detected in calvin control and gives proper REST response
            _exit_with_error(org_cb)

        security.check_security_policy(CalvinCB(_handle_policy_decision,
                                                source_text,
                                                appname,
                                                verify,
                                                security=security,
                                                org_cb=org_cb),
                                       "application",
                                       signer=signer)

    def _handle_policy_decision(source_text,
                                appname,
                                verify,
                                access_decision,
                                org_cb,
                                security=None):
        if not access_decision:
            _log.error("Access denied")
            # This error reason is detected in calvin control and gives proper REST response
            _exit_with_error(org_cb)

        deployable, issutracker = compile_script(source_text, appname)

        org_cb(deployable, issutracker, security=security)

    #
    # Actual code for compile_script
    #
    appname = appname_from_filename(filename)
    # FIXME: if node is None we bypass security even if enabled. Is that the intention?
    if node is not None and security_enabled():
        if credentials:
            content = Security.verify_signature_get_files(filename,
                                                          skip_file=True)
            # content is ALWAYS a dict if skip_file is True
            content['file'] = source_text
        else:
            content = None
        # FIXME: If cb is None, we will return from this method with None instead of a tuple, failing silently
        sec = Security(node)
        sec.authenticate_subject(credentials,
                                 callback=CalvinCB(
                                     _handle_authentication_decision,
                                     source_text,
                                     appname,
                                     verify,
                                     security=sec,
                                     org_cb=cb,
                                     content=content))
        return

    #
    # We get here if node is None, or security is disabled
    #
    # This used to be
    # _handle_policy_decision(source_text, filename, verify, access_decision=True, security=None, org_cb=cb)
    # but since _handle_policy_decision is called with access_decision=True, security=None only compile_script would be called
    deployable, issuetracker = compile_script(source_text, appname)
    cb(deployable, issuetracker, security=None)
Example #14
0
    def wrapper(self, handle, connection, match, data, hdr):
        def _unauthorized_response():
            self.send_response(handle,
                               connection,
                               None,
                               status=calvinresponse.UNAUTHORIZED)

        def _handle_authentication_decision(authentication_decision):
            _log.debug(
                "control_apis.authentication::_handle_authentication_decision, authentication_decision={}"
                .format(authentication_decision))
            if not authentication_decision:
                _unauthorized_response()
                return
            try:
                self.security.check_security_policy(
                    CalvinCB(_handle_policy_decision),
                    element_type="control_interface",
                    element_value=arguments['func'].__name__)
            except Exception as exc:
                _log.exception(
                    "Failed to check security policy, exc={}".format(exc))
                _unauthorized_response()

        def _handle_policy_decision(access_decision):
            if not access_decision:
                _unauthorized_response()
                return
            # Yay! We made it!
            func(self, handle, connection, match, data, hdr)

        #
        # Exit early if security disabled
        #
        if not security_enabled():
            func(self, handle, connection, match, data, hdr)
            return
        #
        # Verify the request against credentials and policy
        #
        credentials = None
        arguments = {
            'func': func,
            'self': self,
            'handle': handle,
            'connection': connection,
            'match': match,
            'data': data,
            'hdr': hdr
        }
        try:
            if 'authorization' in hdr:
                cred = b64decode(
                    hdr['authorization'].strip('Basic ')).split(':')
                credentials = {'user': cred[0], 'password': cred[1]}
        except TypeError as err:
            _log.error(
                "_verify_permission: Missing or wrongly formatted credentials in request header"
            )
            # Continue without credentials, policy might allow access
        try:
            self.security.authenticate_subject(
                credentials,
                callback=CalvinCB(_handle_authentication_decision))
        except Exception as exc:
            _log.exception(
                "Failed to authenticate the subject, exc={}".format(exc))
            _unauthorized_response()
Example #15
0
def compile_script_check_security(source_text, filename, cb, credentials=None, verify=True, node=None):
    """
    Compile a script and return a tuple (deployable, errors, warnings).

    'credentials' are optional security credentials(?)
    'verify' is deprecated and will be removed
    'node' is the runtime performing security check(?)
    'cb' is a CalvinCB callback

    N.B. If callback 'cb' is given, this method calls cb(deployable, errors, warnings) and returns None
    N.B. If callback 'cb' is given, and method runs to completion, cb is called with additional parameter 'security' (?)
    """

    def _exit_with_error(callback):
        """Helper method to generate a proper error"""
        it = IssueTracker()
        it.add_error("UNAUTHORIZED", info={'status':401})
        callback({}, it)


    def _handle_authentication_decision(source_text, appname, verify, authentication_decision, security, org_cb, content=None):
        if not authentication_decision:
            _log.error("Authentication failed")
            # This error reason is detected in calvin control and gives proper REST response
            _exit_with_error(org_cb)

        verified, signer = security.verify_signature_content(content, "application")
        if not verified:
            # Verification not OK if sign or cert not OK.
            _log.error("Failed application verification")
            # This error reason is detected in calvin control and gives proper REST response
            _exit_with_error(org_cb)

        security.check_security_policy(
            CalvinCB(_handle_policy_decision, source_text, appname, verify, security=security, org_cb=org_cb),
            "application",
            signer=signer
        )

    def _handle_policy_decision(source_text, appname, verify, access_decision, org_cb, security=None):
        if not access_decision:
            _log.error("Access denied")
            # This error reason is detected in calvin control and gives proper REST response
            _exit_with_error(org_cb)

        deployable, issutracker = compile_script(source_text, appname)

        org_cb(deployable, issutracker, security=security)

    #
    # Actual code for compile_script
    #
    appname = appname_from_filename(filename)
    # FIXME: if node is None we bypass security even if enabled. Is that the intention?
    if node is not None and security_enabled():
        if credentials:
            content = Security.verify_signature_get_files(filename, skip_file=True)
            # content is ALWAYS a dict if skip_file is True
            content['file'] = source_text
        else:
            content = None
        # FIXME: If cb is None, we will return from this method with None instead of a tuple, failing silently
        sec = Security(node)
        sec.authenticate_subject(
            credentials,
            callback=CalvinCB(_handle_authentication_decision, source_text, appname, verify, security=sec, org_cb=cb, content=content)
        )
        return

    #
    # We get here if node is None, or security is disabled
    #
    # This used to be
    # _handle_policy_decision(source_text, filename, verify, access_decision=True, security=None, org_cb=cb)
    # but since _handle_policy_decision is called with access_decision=True, security=None only compile_script would be called
    deployable, issuetracker = compile_script(source_text, appname)
    cb(deployable, issuetracker, security=None)