Browse Source

Do much additional checking before executing a process, mainly regarding existence of models and access permissions

Yentl Van Tendeloo 7 years ago
parent
commit
6c2a4ec03c
4 changed files with 168 additions and 1 deletions
  1. 43 0
      bootstrap/core_algorithm.alc
  2. 106 0
      unit/test_all.py
  3. 7 0
      wrappers/classes/modelverse.xml
  4. 12 1
      wrappers/modelverse_SCCD.py

+ 43 - 0
bootstrap/core_algorithm.alc

@@ -1133,6 +1133,7 @@ String function cmd_model_add(type : String, name : String, code : String):
 String function cmd_process_execute(process : String, mapping : Element):
 	// Execute a process model until it reaches termination
 	String process_id
+	String name
 
 	process_id = get_entry_id(process)
 	if (process_id != ""):
@@ -1146,6 +1147,48 @@ String function cmd_process_execute(process : String, mapping : Element):
 				String ver
 				ver = conformance_scd(pm)
 				if (ver == "OK"):
+					Element all_data
+					String data
+					Element names
+					all_data = allInstances(pm, "Data")
+					names = set_create()
+					while (set_len(all_data) > 0):
+						data = set_pop(all_data)
+						set_add(names, read_attribute(pm, data, "name"))
+						if (set_len(allIncomingAssociationInstances(pm, data, "Consumes")) > 0):
+							name = read_attribute(pm, data, "name")
+							// It is used somewhere for consumption: require read permissions
+							// First check if this is a defined name
+							if (dict_in(mapping, name)):
+								// Yes, it is defined seperately, so we have to check
+								if (get_entry_id(mapping[name]) != ""):
+									// Existing model, so have to check
+									if (bool_not(allow_read(current_user_id, get_entry_id(mapping[name])))):
+										// Not readable model, while it has to be read: error
+										return "Read permission denied to: " + cast_string(mapping[name])!
+									if (read_type(core, mapping[name]) == "Folder"):
+										return "Not a model: " + cast_string(mapping[name])!
+						if (set_len(allIncomingAssociationInstances(pm, data, "Produces")) > 0):
+							name = read_attribute(pm, data, "name")
+							// It is used somewhere for consumption: require read permissions
+							// First check if this is a defined name
+							if (dict_in(mapping, name)):
+								// Yes, it is defined seperately, so we have to check
+								if (get_entry_id(mapping[name]) != ""):
+									// Existing model, so have to check
+									if (bool_not(allow_write(current_user_id, get_entry_id(mapping[name])))):
+										// Not readable model, while it has to be read: error
+										return "Write permission denied to: " + cast_string(mapping[name])!
+									if (read_type(core, mapping[name]) == "Folder"):
+										return "Not a model: " + cast_string(mapping[name])!
+
+					// Now check all keys in the mapping
+					Element map_keys
+					map_keys = dict_keys(mapping)
+					if (set_len(set_difference(map_keys, names)) > 0):
+						return "Signature mismatch: " + cast_string(set_pop(set_difference(map_keys, names)))!
+
+					// Now execute!
 					enact_PM(pm, mapping)
 					return "Success"!
 				else:

+ 106 - 0
unit/test_all.py

@@ -5270,6 +5270,112 @@ class TestModelverse(unittest.TestCase):
         except NotAProcess:
             pass
 
+        # Test non-existing key in binding
+        model_add("users/user/test/a", "formalisms/ProcessModel", """
+            Data a {
+                name = "abc"
+                type = "def"
+            }
+            Exec b {
+                name = "b"
+            }
+            Consumes (b, a) {
+                name = "a"
+            }
+            Start {}
+            Finish {}
+            """)
+        try:
+            process_execute("users/user/test/a", {"def": "users/user/test/b"})
+            self.fail()
+        except SignatureMismatch:
+            pass
+
+        # Test non-readable model in binding
+        model_add("users/user/test/b", "formalisms/ProcessModel", """
+            Data a {
+                name = "abc"
+                type = "def"
+            }
+            Exec b {
+                name = "b"
+            }
+            Consumes (b, a) {
+                name = "a"
+            }
+            Start {}
+            Finish {}
+            """)
+        try:
+            process_execute("users/user/test/b", {"abc": "administration/core"})
+            self.fail()
+        except ReadPermissionDenied:
+            pass
+
+        # Test non-writeable model in binding
+        model_add("users/user/test/c", "formalisms/ProcessModel", """
+            Data a {
+                name = "abc"
+                type = "def"
+            }
+            Exec b {
+                name = "b"
+            }
+            Produces (b, a) {
+                name = "a"
+            }
+            Start {}
+            Finish {}
+            """)
+        try:
+            process_execute("users/user/test/c", {"abc": "formalisms/SimpleClassDiagrams"})
+            self.fail()
+        except WritePermissionDenied:
+            pass
+
+        # Test combination of both produce and consume
+        model_add("users/user/test/d", "formalisms/ProcessModel", """
+            Data a {
+                name = "abc"
+                type = "def"
+            }
+            Exec b {
+                name = "b"
+            }
+            Produces (b, a) {
+                name = "a"
+            }
+            Consumes (b, a) {
+                name = "a"
+            }
+            Start {}
+            Finish {}
+            """)
+        try:
+            process_execute("users/user/test/d", {"abc": "formalisms/SimpleClassDiagrams"})
+            self.fail()
+        except WritePermissionDenied:
+            pass
+
+        # Test unused data
+        model_add("users/user/test/e", "formalisms/ProcessModel", """
+            Data a {
+                name = "abc"
+                type = "def"
+            }
+            Start b {}
+            Finish c {}
+            Next (b, c) {}
+            """)
+        process_execute("users/user/test/e", {"abc": "administration/core"})
+
+        # Test folder model in binding
+        try:
+            process_execute("users/user/test/a", {"abc": "formalisms"})
+            self.fail()
+        except NotAModel:
+            pass
+
     def test_modelling(self):
         # Add a model
         model_add("users/user/test/Empty", "formalisms/SimpleClassDiagrams")

+ 7 - 0
wrappers/classes/modelverse.xml

@@ -1651,6 +1651,13 @@
                         </raise>
                     </transition>
 
+                    <transition cond="self.expect_response_partial('Signature mismatch: ', pop=False)" target="../wait_for_action/history">
+                        <raise event="exception">
+                            <parameter expr="'SignatureMismatch'"/>
+                            <parameter expr="self.responses.pop(0)"/>
+                        </raise>
+                    </transition>
+
                     <transition cond="self.expect_response_partial('', pop=False)" target="../wait_for_action/history">
                         <script>
                             print("Unknown Error: " + self.responses[0])

+ 12 - 1
wrappers/modelverse_SCCD.py

@@ -1953,6 +1953,11 @@ class Modelverse(RuntimeClassBase):
         _initialized_behaviour_operations_38.setTrigger(None)
         _initialized_behaviour_operations_38.setGuard(self._initialized_behaviour_operations_38_guard)
         self.states["/initialized/behaviour/operations"].addTransition(_initialized_behaviour_operations_38)
+        _initialized_behaviour_operations_39 = Transition(self, self.states["/initialized/behaviour/operations"], [self.states["/initialized/behaviour/wait_for_action/history"]])
+        _initialized_behaviour_operations_39.setAction(self._initialized_behaviour_operations_39_exec)
+        _initialized_behaviour_operations_39.setTrigger(None)
+        _initialized_behaviour_operations_39.setGuard(self._initialized_behaviour_operations_39_guard)
+        self.states["/initialized/behaviour/operations"].addTransition(_initialized_behaviour_operations_39)
         
         # transition /initialized/behaviour/operations/store_on_scripted/transformation_add
         _initialized_behaviour_operations_store_on_scripted_transformation_add_0 = Transition(self, self.states["/initialized/behaviour/operations/store_on_scripted/transformation_add"], [self.states["/initialized/behaviour/wait_for_action/megamodelling"]])
@@ -2566,10 +2571,16 @@ class Modelverse(RuntimeClassBase):
         return self.expect_response_partial('Signature mismatch in operation for tag: ', pop=False)
     
     def _initialized_behaviour_operations_38_exec(self, parameters):
+        self.raiseInternalEvent(Event("exception", None, ['SignatureMismatch', self.responses.pop(0)]))
+    
+    def _initialized_behaviour_operations_38_guard(self, parameters):
+        return self.expect_response_partial('Signature mismatch: ', pop=False)
+    
+    def _initialized_behaviour_operations_39_exec(self, parameters):
         print("Unknown Error: " + self.responses[0])
         self.raiseInternalEvent(Event("exception", None, ['UnknownError', 'Error: %s' % self.responses.pop(0)]))
     
-    def _initialized_behaviour_operations_38_guard(self, parameters):
+    def _initialized_behaviour_operations_39_guard(self, parameters):
         return self.expect_response_partial('', pop=False)
     
     def _initialized_behaviour_operations_store_on_scripted_transformation_add_0_exec(self, parameters):