You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesatee.apache.org by ur...@apache.org on 2019/11/20 02:35:30 UTC

[incubator-mesatee] branch acs updated: More acs implementation

This is an automated email from the ASF dual-hosted git repository.

uraj pushed a commit to branch acs
in repository https://gitbox.apache.org/repos/asf/incubator-mesatee.git


The following commit(s) were added to refs/heads/acs by this push:
     new 99d6f30  More acs implementation
99d6f30 is described below

commit 99d6f30501479171cb129c4bc1af6e2975154568
Author: Pei Wang <wa...@baidu.com>
AuthorDate: Thu Nov 7 14:41:49 2019 -0800

    More acs implementation
---
 CMakeLists.txt                                     |   2 +-
 Makefile                                           |   2 +-
 mesatee_core/src/error.rs                          |   7 +-
 mesatee_services/acs/client/src/acs_client.rs      | 115 +++++++--
 mesatee_services/acs/client/src/lib.rs             |   2 +-
 mesatee_services/acs/model.conf                    |  41 ++--
 mesatee_services/acs/proto/Cargo.toml              |   6 +-
 mesatee_services/acs/proto/src/lib.rs              |   2 +-
 mesatee_services/acs/proto/src/proto.rs            | 156 +++---------
 .../acs/python/{perm.py => acs_engine.py}          | 266 ++++++++------------
 mesatee_services/acs/python/acs_engine_test.py     |  82 +++++++
 mesatee_services/acs/python/ffi.py                 |   8 +-
 mesatee_services/acs/sgx_trusted_lib/src/acs.rs    | 254 ++++++++++++++------
 mesatee_services/acs/sgx_trusted_lib/src/sgx.rs    |  32 +--
 tests/functional_test.sh                           |  10 +-
 tests/functional_test/sgx_trusted_lib/Cargo.toml   |   3 +-
 tests/functional_test/sgx_trusted_lib/src/sgx.rs   |   1 +
 .../sgx_trusted_lib/src/tests/acs_test.rs          | 267 +++++++++++++++++++++
 .../sgx_trusted_lib/src/tests/common_setup.rs      |   7 +
 .../sgx_trusted_lib/src/tests/mod.rs               |   1 +
 20 files changed, 848 insertions(+), 416 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6f8de9a..3d54dcb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,7 +24,7 @@ dbscan neural_net naive_bayes gbdt mesatee_cli)
 set(UNIX_LIBS mesatee_sdk protected_fs_rs)
 # need relative path for SGX_MODULE_PATHS to find Enclave.config.xml
 set(SGX_MODULE_PATHS mesatee_services/kms mesatee_services/tdfs mesatee_services/tms
-    mesatee_services/fns tests/functional_test)
+    mesatee_services/fns mesatee_services/acs tests/functional_test)
 # SGX_MODULES is generated from SGX_MODULE_PATHS
 # set(SGX_MODULES kms tdfs tms fns functional_test)
 # ================ VARIABLES FOR MANUAL CHANGE END ================
diff --git a/Makefile b/Makefile
index ee3d9be..7fcca99 100644
--- a/Makefile
+++ b/Makefile
@@ -243,7 +243,7 @@ pycomponent: $(OUT_DIR)/libpycomponent.a
 $(OUT_DIR)/libpycomponent.a: $(OUT_DIR)/acs_py_enclave.o
 	cd $(OUT_DIR) && ar rcs $@ $?
 
-$(OUT_DIR)/acs_py_enclave.c: mesatee_services/acs/python/ffi.py mesatee_services/acs/python/perm.py prep
+$(OUT_DIR)/acs_py_enclave.c: mesatee_services/acs/python/ffi.py mesatee_services/acs/python/acs_engine.py prep
 	env PYTHONPATH=$(THIRD_PARTY_DIR)/mesapy/sgx PYPY_FFI_OUTDIR=$(OUT_DIR) pypy $<
 
 $(OUT_DIR)/acs_py_enclave.o: $(OUT_DIR)/acs_py_enclave.c
diff --git a/mesatee_core/src/error.rs b/mesatee_core/src/error.rs
index fb67d9c..6c60630 100644
--- a/mesatee_core/src/error.rs
+++ b/mesatee_core/src/error.rs
@@ -113,8 +113,10 @@ pub enum ErrorKind {
     IPCError,
     /// IAS client key or cert not available
     IASClientKeyCertError,
-    /// No valid worker for the task,
+    /// No valid worker for the task
     NoValidWorkerError,
+    /// Unhandled MesaPy exception encountered
+    MesaPyError,
     /// Others.
     Unknown,
 }
@@ -186,6 +188,7 @@ impl ErrorKind {
                 "intel attestation service client key/certificate unavailable error"
             }
             ErrorKind::NoValidWorkerError => "no valid worker error",
+            ErrorKind::MesaPyError => "unhandled mesapy exception",
             ErrorKind::Unknown => "unknown error",
         }
     }
@@ -236,6 +239,7 @@ impl From<u32> for Error {
             0x0000_1010 => ErrorKind::IPCError,
             0x0000_1011 => ErrorKind::IASClientKeyCertError,
             0x0000_1012 => ErrorKind::NoValidWorkerError,
+            0x0000_1013 => ErrorKind::MesaPyError,
             _ => ErrorKind::Unknown,
         };
 
@@ -282,6 +286,7 @@ impl Into<u32> for Error {
             ErrorKind::IPCError => 0x0000_1010,
             ErrorKind::IASClientKeyCertError => 0x0000_1011,
             ErrorKind::NoValidWorkerError => 0x0000_1012,
+            ErrorKind::MesaPyError => 0x0000_1013,
             ErrorKind::Unknown => 0xffff_ffff,
         }
     }
diff --git a/mesatee_services/acs/client/src/acs_client.rs b/mesatee_services/acs/client/src/acs_client.rs
index 6f41da9..9a1ceff 100644
--- a/mesatee_services/acs/client/src/acs_client.rs
+++ b/mesatee_services/acs/client/src/acs_client.rs
@@ -16,50 +16,135 @@
 #[cfg(feature = "mesalock_sgx")]
 use std::prelude::v1::*;
 
-use acs_proto::{CreateKeyResponse, DeleteKeyResponse, GetKeyResponse, KMSRequest, KMSResponse};
+use acs_proto::*;
 use mesatee_core::config::{OutboundDesc, TargetDesc};
 use mesatee_core::rpc::channel::SgxTrustedChannel;
 use mesatee_core::{Error, ErrorKind, Result};
 
-pub struct KMSClient {
-    channel: SgxTrustedChannel<KMSRequest, KMSResponse>,
+use std::collections::HashSet;
+
+pub struct ACSClient {
+    channel: SgxTrustedChannel<ACSRequest, ACSResponse>,
 }
 
-impl KMSClient {
+impl ACSClient {
     pub fn new(target: TargetDesc) -> Result<Self> {
         let addr = target.addr;
         let channel = match target.desc {
             OutboundDesc::Sgx(enclave_addr) => {
-                SgxTrustedChannel::<KMSRequest, KMSResponse>::new(addr, enclave_addr)?
+                SgxTrustedChannel::<ACSRequest, ACSResponse>::new(addr, enclave_addr)?
             }
         };
-        Ok(KMSClient { channel })
+        Ok(ACSClient { channel })
     }
 
-    pub fn request_create_key(&mut self) -> Result<CreateKeyResponse> {
-        let req = KMSRequest::new_create_key();
+    pub fn enforce_task_launch(
+        &mut self,
+        task: String,
+        participants: HashSet<String>,
+    ) -> Result<bool> {
+        let req = ACSRequest::Enforce(EnforceRequest::LaunchTask(task, participants));
         let resp = self.channel.invoke(req)?;
         match resp {
-            KMSResponse::Create(resp) => Ok(resp),
+            ACSResponse::Enforce(allow) => Ok(allow),
             _ => Err(Error::from(ErrorKind::RPCResponseError)),
         }
     }
 
-    pub fn request_get_key(&mut self, key_id: &str) -> Result<GetKeyResponse> {
-        let req = KMSRequest::new_get_key(key_id);
+    pub fn enforce_data_access(
+        &mut self,
+        task: String,
+        data: String
+    ) -> Result<bool> {
+        let req = ACSRequest::Enforce(EnforceRequest::AccessData(task, data));
         let resp = self.channel.invoke(req)?;
         match resp {
-            KMSResponse::Get(resp) => Ok(resp),
+            ACSResponse::Enforce(allow) => Ok(allow),
             _ => Err(Error::from(ErrorKind::RPCResponseError)),
         }
     }
 
-    pub fn request_del_key(&mut self, key_id: &str) -> Result<DeleteKeyResponse> {
-        let req = KMSRequest::new_del_key(key_id);
+    pub fn enforce_data_deletion(
+        &mut self,
+        usr: String,
+        data: String,
+    ) -> Result<bool> {
+        let req = ACSRequest::Enforce(EnforceRequest::DeleteData(usr, data));
         let resp = self.channel.invoke(req)?;
         match resp {
-            KMSResponse::Delete(resp) => Ok(resp),
+            ACSResponse::Enforce(allow) => Ok(allow),
             _ => Err(Error::from(ErrorKind::RPCResponseError)),
         }
     }
+    
+    pub fn enforce_script_access(
+        &mut self,
+        task: String,
+        script: String,
+    ) -> Result<bool> {
+        let req = ACSRequest::Enforce(EnforceRequest::AccessScript(task, script));
+        let resp = self.channel.invoke(req)?;
+        match resp {
+            ACSResponse::Enforce(allow) => Ok(allow),
+            _ => Err(Error::from(ErrorKind::RPCResponseError)),
+        }
+    }
+    
+    pub fn enforce_script_deletion(
+        &mut self,
+        usr: String,
+        script: String
+    ) -> Result<bool> {
+        let req = ACSRequest::Enforce(EnforceRequest::DeleteScript(usr, script));
+        let resp = self.channel.invoke(req)?;
+        match resp {
+            ACSResponse::Enforce(allow) => Ok(allow),
+            _ => Err(Error::from(ErrorKind::RPCResponseError)),
+        }
+    }
+
+    fn _announce_terms(&mut self, facts: Vec<AccessControlTerms>) -> Result<()> {
+        let req = ACSRequest::Announce(AnnounceRequest { facts });
+        let resp = self.channel.invoke(req)?;
+        match resp {
+            ACSResponse::Announce => Ok(()),
+            _ => Err(Error::from(ErrorKind::RPCResponseError)),
+        }
+    }
+
+    pub fn announce_task_creation(
+        &mut self,
+        task: String,
+        creator: String,
+        participants: &HashSet<String>,
+    ) -> Result<()> {
+        let mut facts = Vec::with_capacity(1 + participants.len());
+        for par in participants {
+            facts.push(AccessControlTerms::TaskParticipant(task.clone(), par.clone()));
+        }
+        facts.push(AccessControlTerms::TaskCreator(task, creator));
+        self._announce_terms(facts)
+    }
+
+    pub fn announce_data_creation(
+        &mut self,
+        data: String,
+        creator: String,
+    ) -> Result<()> {
+        self._announce_terms(std::vec!(AccessControlTerms::DataOwner(data, creator)))
+    }
+
+    pub fn announce_script_creation(
+        &mut self,
+        script: String,
+        creator: String,
+        is_public: bool,
+    ) -> Result<()> {
+        let mut terms = Vec::new();
+        if is_public {
+            terms.push(AccessControlTerms::IsPublicScript(script.clone()))
+        }
+        terms.push(AccessControlTerms::ScriptOwner(script, creator));
+        self._announce_terms(terms)
+    }
 }
diff --git a/mesatee_services/acs/client/src/lib.rs b/mesatee_services/acs/client/src/lib.rs
index a5d844c..f774564 100644
--- a/mesatee_services/acs/client/src/lib.rs
+++ b/mesatee_services/acs/client/src/lib.rs
@@ -18,4 +18,4 @@
 extern crate sgx_tstd as std;
 
 mod acs_client;
-pub use acs_client::KMSClient;
+pub use acs_client::ACSClient;
diff --git a/mesatee_services/acs/model.conf b/mesatee_services/acs/model.conf
index 2ab31d9..7e30003 100644
--- a/mesatee_services/acs/model.conf
+++ b/mesatee_services/acs/model.conf
@@ -1,21 +1,32 @@
-# request definition
-[request_definition]
-r = sub, obj, act
-r2 = sub, obj
-r3 = task, participants
+[requests]
+launch_task = task, participants
+access_data = task, data
+delete_data = usr, data
+access_script = task, script
+delete_script = usr, script
 
-[policy_definition]
-p = sub, obj, act
+[terms]
+task_creator = task, usr
+task_participant = task, usr
 
-[term_definition]
-g = sub, grp
-c = task, usr
+script_owner = script, usr
+is_public_script = script
 
-[policy_effect]
-allow-and-deny
+data_owner = data, usr
 
 [matchers]
-r = (r.sub == p.sub and r.obj == p.obj and r.act == p.act) or (g(r.sub, 'admin') and not g(r.sub, 'blklist'))
-r2 = r2.sub == r2.obj.Owner
-r3 = all(usr in r3.participants for usr, in c(r3.task, _))
+# All participants must approve task launch
+launch_task = task_participant(launch_task.task, _) <= launch_task.participants
 
+# All participants must approve data access
+access_data = data_owner(access_data.data, _) <= task_participant(access_data.task, _)
+
+# Any creator of the data can request for deletion
+delete_data = data_owner(delete_data.data, delete_data.usr)
+
+access_script = \
+    is_public_script(access_script.script) or \
+    script_owner(access_script.script, _) <= task_participant(access_script.task, _)
+
+# Only creator of the script can request for deletion
+delete_script = script_owner(delete_script.script, delete_script.usr)
\ No newline at end of file
diff --git a/mesatee_services/acs/proto/Cargo.toml b/mesatee_services/acs/proto/Cargo.toml
index 7676954..55be836 100644
--- a/mesatee_services/acs/proto/Cargo.toml
+++ b/mesatee_services/acs/proto/Cargo.toml
@@ -12,14 +12,12 @@ mesalock_sgx = ["sgx_tstd", "mesatee_core/mesalock_sgx"]
 cov = ["sgx_cov"]
 
 [dependencies]
-cfg-if       = { version = "0.1.9" }
+mesatee_core = { version = "0.1.0" }
 serde        = { version = "1.0.39" }
 serde_json   = { version = "1.0.39" }
 serde_derive = { version = "1.0.92" }
-rand         = { version = "0.7.0" }
-base64       = { version = "0.10.1" }
+uuid         = { version = "0.7.4", features = ["v4"] }
 
-mesatee_core = { version = "0.1.0" }
 sgx_cov      = { version = "0.1.0", optional = true }
 sgx_tstd     = { version = "1.0.9", features = ["net", "backtrace"], optional = true }
 sgx_types    = { version = "1.0.9" }
diff --git a/mesatee_services/acs/proto/src/lib.rs b/mesatee_services/acs/proto/src/lib.rs
index ddee753..ff8f727 100644
--- a/mesatee_services/acs/proto/src/lib.rs
+++ b/mesatee_services/acs/proto/src/lib.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 #![cfg_attr(feature = "mesalock_sgx", no_std)]
 #[cfg(feature = "mesalock_sgx")]
-#[macro_use]
+
 extern crate sgx_tstd as std;
 
 mod proto;
diff --git a/mesatee_services/acs/proto/src/proto.rs b/mesatee_services/acs/proto/src/proto.rs
index 3cc2bf4..78c63f1 100644
--- a/mesatee_services/acs/proto/src/proto.rs
+++ b/mesatee_services/acs/proto/src/proto.rs
@@ -16,148 +16,60 @@
 #[cfg(feature = "mesalock_sgx")]
 use std::prelude::v1::*;
 
-use mesatee_core;
-use mesatee_core::Result;
+use std::collections::HashSet;
+
 use serde_derive::*;
 
 #[derive(Clone, Serialize, Deserialize, Debug)]
 #[serde(tag = "type")]
-pub enum KMSRequest {
-    Create(CreateKeyRequest),
-    Get(GetKeyRequest),
-    Delete(DeleteKeyRequest),
+pub enum ACSRequest {
+    Enforce(EnforceRequest),
+    Announce(AnnounceRequest),
 }
 
 #[derive(Clone, Serialize, Deserialize, Debug)]
-#[serde(tag = "type")]
-pub enum KMSResponse {
-    Create(CreateKeyResponse),
-    Get(GetKeyResponse),
-    Delete(DeleteKeyResponse),
-}
-
-#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
-pub struct AEADKeyConfig {
-    #[serde(with = "base64_coder")]
-    pub key: Vec<u8>,
-    #[serde(with = "base64_coder")]
-    pub nonce: Vec<u8>,
-    #[serde(with = "base64_coder")]
-    pub ad: Vec<u8>,
+pub enum ACSResponse {
+    Enforce(bool),
+    Announce,
 }
 
 #[derive(Clone, Serialize, Deserialize, Debug)]
-pub struct CreateKeyRequest {}
+pub enum EnforceRequest {
+    // launch_task = task, participants
+    LaunchTask(String, HashSet<String>),
 
-#[derive(Clone, Serialize, Deserialize, Debug)]
-pub struct CreateKeyResponse {
-    pub key_id: String,
-    pub config: AEADKeyConfig,
-}
+    // access_data = task, data
+    AccessData(String, String),
+    
+    // delete_data = usr, data
+    DeleteData(String, String),
 
-#[derive(Clone, Serialize, Deserialize, Debug)]
-pub struct GetKeyRequest {
-    pub key_id: String,
-}
+    // access_script = task, script
+    AccessScript(String, String),
 
-#[derive(Clone, Serialize, Deserialize, Debug)]
-pub struct GetKeyResponse {
-    pub config: AEADKeyConfig,
+    // delete_script = usr, script
+    DeleteScript(String, String),
 }
 
 #[derive(Clone, Serialize, Deserialize, Debug)]
-pub struct DeleteKeyRequest {
-    pub key_id: String,
+pub struct AnnounceRequest {
+    pub facts: Vec<AccessControlTerms>,
 }
 
 #[derive(Clone, Serialize, Deserialize, Debug)]
-pub struct DeleteKeyResponse {
-    pub config: AEADKeyConfig,
-}
-
-impl KMSRequest {
-    pub fn new_create_key() -> KMSRequest {
-        KMSRequest::Create(CreateKeyRequest {})
-    }
-
-    pub fn new_get_key(key_id: &str) -> KMSRequest {
-        let req = GetKeyRequest {
-            key_id: key_id.to_owned(),
-        };
-        KMSRequest::Get(req)
-    }
-
-    pub fn new_del_key(key_id: &str) -> KMSRequest {
-        let req = DeleteKeyRequest {
-            key_id: key_id.to_owned(),
-        };
-        KMSRequest::Delete(req)
-    }
-}
-
-impl KMSResponse {
-    pub fn new_create_key(key_id: &str, key: &AEADKeyConfig) -> KMSResponse {
-        let resp = CreateKeyResponse {
-            key_id: key_id.to_owned(),
-            config: key.clone(),
-        };
-        KMSResponse::Create(resp)
-    }
-
-    pub fn new_get_key(config: &AEADKeyConfig) -> KMSResponse {
-        let resp = GetKeyResponse {
-            config: config.clone(),
-        };
-        KMSResponse::Get(resp)
-    }
-
-    pub fn new_del_key(config: &AEADKeyConfig) -> KMSResponse {
-        let resp = DeleteKeyResponse {
-            config: config.clone(),
-        };
-        KMSResponse::Delete(resp)
-    }
-}
-
-impl AEADKeyConfig {
-    pub fn new() -> Result<Self> {
-        use rand::prelude::RngCore;
-
-        let mut key_config = AEADKeyConfig {
-            key: vec![0; 32],
-            nonce: vec![0; 12],
-            ad: vec![0; 5],
-        };
-
-        let mut rng = rand::thread_rng();
-        rng.fill_bytes(&mut key_config.key);
-        rng.fill_bytes(&mut key_config.nonce);
-        rng.fill_bytes(&mut key_config.ad);
-
-        Ok(key_config)
-    }
-}
-
-mod base64_coder {
-    // Insert std prelude in the top for the sgx feature
-    #[cfg(feature = "mesalock_sgx")]
-    use std::prelude::v1::*;
+pub enum AccessControlTerms {
+    // task_creator = task, usr
+    TaskCreator(String, String),
+    
+    // task_participant = task, usr
+    TaskParticipant(String, String),
 
-    extern crate base64;
-    use serde::{de, Deserialize, Deserializer, Serializer};
+    // data_owner = data, usr
+    DataOwner(String, String),
 
-    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        serializer.serialize_str(&base64::encode(bytes))
-    }
+    // script_owner = script, usr
+    ScriptOwner(String, String),
 
-    pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        let s = <&str>::deserialize(deserializer)?;
-        base64::decode(s).map_err(de::Error::custom)
-    }
+    // is_public_script = script
+    IsPublicScript(String),
 }
diff --git a/mesatee_services/acs/python/perm.py b/mesatee_services/acs/python/acs_engine.py
similarity index 71%
rename from mesatee_services/acs/python/perm.py
rename to mesatee_services/acs/python/acs_engine.py
index 1d2a915..4a12bf4 100644
--- a/mesatee_services/acs/python/perm.py
+++ b/mesatee_services/acs/python/acs_engine.py
@@ -158,11 +158,8 @@ class Parser(object):
     def parse_from(self, stream):
         n_state = self(stream)
         if not n_state:
-            print n_state.stream, n_state.payload
             raise ParsingError(n_state.stream, n_state.payload)
         elif not n_state.stream.end():
-            print n_state.stream, 'trailing unparsable input'
-            print n_state.stream.pos()
             raise ParsingError(n_state.stream, 'trailing unparsable input')
         return n_state
 
@@ -184,15 +181,22 @@ class Parser(object):
     def __invert__(self):
         return Rep(self)
 
+    def __neg__(self):
+        return Optional(self)
+
     def __pow__(self, f):
         return Apply(self, f)
 
-class Epsilon(Parser):
-    def __init__(self):
-        super(Epsilon, self).__init__()
+class Optional(Parser):
+    def __init__(self, opt):
+        super(Optional, self).__init__()
+        self.__opt = opt
 
     def __call__(self, stream):
-        return State(stream)
+        n_state = self.__opt(stream)
+        if n_state:
+            return n_state.fmap(lambda x: Left(x))
+        return State(stream, Right(None))
 
 class StrLiteral(Parser):
     def __init__(self, string):
@@ -375,18 +379,6 @@ Definition = Identifier + Equal + IdTuple + NewLine
 Relation = Identifier + Equal + IdTuple + NewLine
 Relation = Relation ** (lambda x: (x[0], 1 + len(x[1][1])))
 
-POLICY_EFFECT_ALLOW_OVERRIDE = 'allow-override'
-POLICY_EFFECT_DENY_OVERRIDE  = 'deny-override'
-POLICY_EFFECT_ALLOW_AND_DENY = 'allow-and-deny'
-
-POLICY_EFFECTS = [
-    POLICY_EFFECT_ALLOW_OVERRIDE,
-    POLICY_EFFECT_DENY_OVERRIDE,
-    POLICY_EFFECT_ALLOW_AND_DENY
-]
-
-PolicyEft = one_of(map(StrLiteral, POLICY_EFFECTS)) + NewLine
-
 def pyparser_matcher(text, pos):
     """syntactically correct python code"""
     line_end = text.find('\n', pos)
@@ -401,19 +393,15 @@ def pyparser_matcher(text, pos):
 PyExpr = CustomMatcher(pyparser_matcher)
 Matcher = Identifier + Equal + PyExpr + NewLine
 
-RequestDefHeader = StrLiteral('[request_definition]') + NewLine
-TermDefHeader    = StrLiteral('[term_definition]') + NewLine
-PolicyDefHeader  = StrLiteral('[policy_definition]') + NewLine
-PolicyEftHeader  = StrLiteral('[policy_effect]') + NewLine
+RequestDefHeader = StrLiteral('[requests]') + NewLine
+TermDefHeader    = StrLiteral('[terms]') + NewLine
 MatchersHeader   = StrLiteral('[matchers]') + NewLine
 
 RequestDefSec = RequestDefHeader.ignore() + ~Definition
 TermDefSec = TermDefHeader.ignore() + ~Definition
-PolicyDefSec = PolicyDefHeader.ignore() + ~Definition
-PolicyEftSec = PolicyEftHeader.ignore() + PolicyEft
 MatchersSec = MatchersHeader.ignore() + ~Matcher
 
-ModelDef = (RequestDefSec + PolicyDefSec + TermDefSec + PolicyEftSec + MatchersSec) ** flatten
+ModelDef = (RequestDefSec + TermDefSec + MatchersSec) ** flatten
 
 def preprocess(conf):
     # process escaped line breaks
@@ -454,7 +442,7 @@ class InvalidModelDefinition(Exception):
         msg = 'matcher(s) defined for unknown request type(s): {}'
         return InvalidModelDefinition(msg.format(', '.join(unknown_requests)))
 
-class Policy(object):
+class Request(object):
     def __init__(self, attrs, vals):
         assert len(attrs) == len(vals)
         self.__named_attrs = attrs
@@ -462,7 +450,7 @@ class Policy(object):
             setattr(self, attr, val)
 
     def __repr__(self):
-        parts = ['Policy {\n']
+        parts = ['Request {\n']
         for attr in self.__named_attrs:
             parts.append('  ')
             parts.append(attr)
@@ -472,11 +460,31 @@ class Policy(object):
         parts.append('}\n')
         return ''.join(parts)
 
+class QueryResult(object):
+    def __init__(self, generator):
+        self.__gen = generator
+
+    def __iter__(self):
+        return self.__gen
+
+    def __le__(self, iterable):
+        return set(self) <= set(iterable)
+
+    def __lt__(self, iterable):
+        return set(self) < set(iterable)
+
+    def __ge__(self, iterable):
+        return set(self) >= set(iterable)
+
+    def __gt__(self, iterable):
+        return set(self) > set(iterable)
+
 class Term(object):
-    WILDCARD = object()
+    PLACEHOLDER = object()
+    WILDCARD = None
     def __init__(self, arity):
         self.__arity = arity
-        self.__facts = []
+        self.__facts = set()
 
     def add_facts(self, facts):
         for fact in facts:
@@ -486,15 +494,16 @@ class Term(object):
         assert len(fact) == self.__arity
         if not isinstance(fact, tuple):
             fact = tuple(fact)
-        self.__facts.append(fact)
+        self.__facts.add(fact)
 
     def __call__(self, *args):
         assert len(args) == self.__arity
         # When all arguments are concrete, calling a term just returns boolean results
         # indicating whether the called tuple is part of the known facts
-        if not any(arg is Term.WILDCARD for arg in args):
+        n_placeholders = sum(arg is Term.PLACEHOLDER for arg in args)
+        if not n_placeholders:
             return any(all(a == b for a, b in zip(fact, args)) for fact in self.__facts)
-        # If arguments contain one or more wildcards, calling a term is more like a
+        # If arguments contain one or more placeholders, calling a term is more like a
         # query. The call returns a generator that iterates all facts that match with
         # the pattern described by the arguments
         def gen():
@@ -502,28 +511,29 @@ class Term(object):
                 rns = []
                 matched = True
                 for a, b in zip(fact, args):
-                    if b is Term.WILDCARD:
+                    if b is Term.PLACEHOLDER:
                         rns.append(a)
                     else:
                         if a != b:
                             matched = False
                             break
                 if matched:
-                    yield rns
-        return gen()
+                    if n_placeholders == 1:
+                        yield rns[0]
+                    else:
+                        yield tuple(rns)
+        return QueryResult(gen())
 
 class Model(object):
     def __init__(self, raw_model):
-        request_def, policy_def, term_def, effect, matchers = raw_model
+        request_def, term_def, matchers = raw_model
         self.__request_template = { r[0]:r[1] for r in request_def }
-        self.__policy_template = { p[0]:p[1] for p in policy_def }
         self.__term_template = { t[0]:t[1] for t in term_def }
-        self.__effect = effect
         self.__matchers = { m[0]:m[1] for m in matchers }
 
         def_sections = zip(
-            ['request_definition', '[policy_definition]', '[term_definition]'],
-            [self.__request_template, self.__policy_template, self.__term_template],
+            ['[requests]', '[terms]'],
+            [self.__request_template, self.__term_template],
         )
 
         n_sec = len(def_sections)
@@ -543,32 +553,17 @@ class Model(object):
         if unknown_requests:
             raise InvalidModelDefinition.unknown_requests(unknown_requests)
 
-        self.__policy_knowledge_base = {
-            policy_name:set() for policy_name in self.__policy_template.keys()
-        }
         self.__term_knowledge_base = {
             term_name:Term(len(term_tpl)) for term_name, term_tpl in self.__term_template.items()
         }
 
-    def add_policies(self, policies):
-        for p in policies:
-            tpl = self.__policy_template[p[0]]
-            self.__policy_knowledge_base[p[0]].add(Policy(tpl, p[1:]))
-
-    def add_policies_from_csv_text(self, csv):
-        self.add_policies([
-            [p.strip() for p in line.split(',')] for line in csv.splitlines() if line
-        ])
-
     def add_term_items(self, term_items):
         for ti in term_items:
-            term = self.__term_knowledge_base[ti[0]]
-            term.add_fact(ti[1:])
+            self.add_term_item(ti[0], ti[1:])
 
-    def add_term_items_from_csv_text(self, csv):
-        self.add_term_items([
-            [p.strip() for p in line.split(',')] for line in csv.splitlines() if line
-        ])
+    def add_term_item(self, term_name, fact):
+        term = self.__term_knowledge_base[term_name]
+        term.add_fact(fact)
 
     def get_matcher_proxy(self, request_type, env):
         def matcher_proxy():
@@ -577,119 +572,60 @@ class Model(object):
 
     def enforce(self, request_type, request_content):
         tpl = self.__request_template[request_type]
-        request_policy = Policy(tpl, request_content)
-
-        has_allow = False
-        
-        def decisions(remaining_policy_keys, env):
-            if not remaining_policy_keys:
-                yield self.get_matcher_proxy(request_type, env)()
-            else:
-                next_key = remaining_policy_keys[0]
-                remaining_policy_keys = remaining_policy_keys[1:]
-                policy_candidates = self.__policy_knowledge_base[next_key]
-                for policy in policy_candidates:
-                    env[next_key] = policy
-                    for d in decisions(remaining_policy_keys, env):
-                        yield d
+        request = Request(tpl, request_content)
 
         enforcer_env = {
-            request_type: request_policy,
+            request_type: request,
             'true': True, 'false': False, 'null': None,
-            '_': Term.WILDCARD
+            '_': Term.PLACEHOLDER,
+            'X': Term.WILDCARD,
         }
         enforcer_env.update(self.__term_knowledge_base)
 
-        for decision in decisions(self.__policy_knowledge_base.keys(), enforcer_env):
-            if decision is True:
-                return True
-        return False
+        return eval(self.__matchers[request_type], enforcer_env)
 
 global_perm_model = None
-if __name__ != '__main__':
+
+if __name__ == '__builtin__':
     from acs_py_enclave import ffi
+else:
+    class ffi:
+        @staticmethod
+        def def_extern():
+            return lambda x: x
+
+        @staticmethod
+        def string(s):
+            return s
 
-    @ffi.def_extern()
-    def mesapy_setup_model(conf):
+@ffi.def_extern()
+def acs_setup_model(conf):
+    try:
         global global_perm_model
-        global_perm_model = Model(parse_model(ffi.string(conf)))
-else:
-    test_model = """
-# request definition
-[request_definition]
-r = sub, obj, act
-r2 = sub, obj
-r3 = task, participants
-
-[policy_definition]
-p = sub, obj, act
-
-[term_definition]
-g = sub, grp
-c = task, usr
-
-[policy_effect]
-allow-and-deny
-
-[matchers]
-r = (r.sub == p.sub and r.obj == p.obj and r.act == p.act) or (g(r.sub, 'admin') and not g(r.sub, 'blklist'))
-r2 = r2.sub == r2.obj.Owner
-r3 = all(usr in r3.participants for usr, in c(r3.task, _))
-"""
-    policy_csv = """
-p, alice, file1, read
-p, bob, file1, read
-p, alice, file2, write
-p, bob, file1, write
-"""
-    term_csv = """
-g, charlie, admin
-g, david, admin
-g, david, blklist
-
-c, task1, usr1
-c, task1, usr2
-c, task1, usr3
-"""
-    raw_model = parse_model(test_model)
-    model = Model(raw_model)
-    model.add_policies_from_csv_text(policy_csv)
-    model.add_term_items_from_csv_text(term_csv)
-
-    assert model.enforce('r', ['alice', 'file1', 'read']) == True
-    assert model.enforce('r', ['alice', 'file1', 'write']) == False
-    assert model.enforce('r', ['alice', 'file2', 'read']) == False
-    assert model.enforce('r', ['alice', 'file2', 'write']) == True
-  
-    assert model.enforce('r', ['bob', 'file1', 'read']) == True
-    assert model.enforce('r', ['bob', 'file1', 'write']) == True
-    assert model.enforce('r', ['bob', 'file2', 'read']) == False
-    assert model.enforce('r', ['bob', 'file2', 'write']) == False
-  
-    assert model.enforce('r', ['charlie', 'file1', 'read']) == True
-    assert model.enforce('r', ['charlie', 'file1', 'write']) == True
-    assert model.enforce('r', ['charlie', 'file2', 'read']) == True
-    assert model.enforce('r', ['charlie', 'file2', 'write']) == True
-  
-    assert model.enforce('r', ['david', 'file1', 'read']) == False
-    assert model.enforce('r', ['david', 'file1', 'write']) == False
-    assert model.enforce('r', ['david', 'file2', 'read']) == False
-    assert model.enforce('r', ['david', 'file2', 'write']) == False
-  
-    class ABACObj(object):
-        def __init__(self, name, owner):
-            self.Name = name
-            self.Owner = owner
-            
-    r2obj = ABACObj('alicedata', 'alice')
-  
-    assert model.enforce('r2', ['charlie', r2obj]) == False
-    assert model.enforce('r2', ['alice', r2obj]) == True
-  
-    assert model.enforce('r3', ['task1', ['usr1', 'usr2', 'usr3']]) == True
-    assert model.enforce('r3', ['task1', ['usr1', 'usr3']]) == False
-    assert model.enforce('r3', ['task1', ['usr1', 'usr2']]) == False
-    assert model.enforce('r3', ['task1', ['usr1']]) == False
-    assert model.enforce('r3', ['task1', []]) == False
-    
-    print 'all access control checks correct!'
+        conf = ffi.string(conf)
+        global_perm_model = Model(parse_model(conf))
+    except:
+        return -1
+    return 0
+
+@ffi.def_extern()
+def acs_enforce_request(request_type, request_content):
+    try:
+        request_type = ffi.string(request_type)
+        # request_content is a list of ffi c strings which are syntactically valid
+        # python primitive-type objects, including strings, integers, foating point
+        # numbers, and lists/dictionaries of primitive-type objects
+        request_content = eval(ffi.string(request_content))
+        return global_perm_model.enforce(request_type, request_content)
+    except:
+        return -1
+
+@ffi.def_extern()
+def acs_announce_fact(term_type, term_fact):
+    try:
+        term_type = ffi.string(term_type)
+        term_fact = eval(ffi.string(term_fact))
+        global_perm_model.add_term_item(term_type, term_fact)
+    except:
+        return -1
+    return 0
diff --git a/mesatee_services/acs/python/acs_engine_test.py b/mesatee_services/acs/python/acs_engine_test.py
new file mode 100644
index 0000000..b951d41
--- /dev/null
+++ b/mesatee_services/acs/python/acs_engine_test.py
@@ -0,0 +1,82 @@
+if __name__ == '__main__':
+    import sys
+    import os
+    from acs_engine import *
+
+    model_path = os.path.join(os.path.dirname(__file__), '../model.conf')
+    test_model = open(model_path).read()
+    acs_setup_model(test_model)
+
+    FUSION_TASK               = "data_fusion"
+    FUSION_TASK_PARTY_1       = "usr_party1"
+    FUSION_TASK_DATA_1        = "data1"
+    FUSION_TASK_PARTY_2       = "usr_party2"
+    FUSION_TASK_DATA_2        = "data2"
+    FUSION_TASK_SCRIPT        = "fusion_script"
+    FUSION_TASK_SCRIPT_WRITER = "usr_party3"
+    PUBLIC_SCRIPT             = "public_script"
+    PUBLIC_SCRIPT_WRITER      = "usr_party4"
+
+    IRRELEVANT_TASK           = "task_irrelevant"
+    IRRELEVANT_PARTY          = "usr_irrelevant"
+    IRRELEVANT_DATA           = "data_irrelevant"
+    
+    acs_announce_fact('task_creator', repr([FUSION_TASK, FUSION_TASK_PARTY_1]))
+    acs_announce_fact('task_participant', repr([FUSION_TASK, FUSION_TASK_PARTY_1]))
+    acs_announce_fact('task_participant', repr([FUSION_TASK, FUSION_TASK_PARTY_2]))
+    
+    acs_announce_fact('data_owner', repr([FUSION_TASK_DATA_1, FUSION_TASK_PARTY_1]))
+    acs_announce_fact('data_owner', repr([FUSION_TASK_DATA_2, FUSION_TASK_PARTY_2]))
+    acs_announce_fact('data_owner', repr([IRRELEVANT_DATA, IRRELEVANT_PARTY]))
+    
+    acs_announce_fact('script_owner', repr([FUSION_TASK_SCRIPT, FUSION_TASK_SCRIPT_WRITER]))
+
+    acs_announce_fact('script_owner', repr([PUBLIC_SCRIPT, PUBLIC_SCRIPT_WRITER]))
+    acs_announce_fact('is_public_script', repr([PUBLIC_SCRIPT]))
+
+
+    assert acs_enforce_request('launch_task', repr([FUSION_TASK, set([FUSION_TASK_PARTY_1, FUSION_TASK_PARTY_2])]))
+    assert not acs_enforce_request('launch_task', repr([FUSION_TASK, set()]))
+    assert not acs_enforce_request('launch_task', repr([FUSION_TASK, set([FUSION_TASK_PARTY_1])]))
+    assert not acs_enforce_request('launch_task', repr([FUSION_TASK, set([FUSION_TASK_PARTY_2])]))
+    
+    assert acs_enforce_request('access_data', repr([FUSION_TASK, FUSION_TASK_DATA_1]))
+    assert acs_enforce_request('access_data', repr([FUSION_TASK, FUSION_TASK_DATA_2]))
+    assert not acs_enforce_request('access_data', repr([FUSION_TASK, IRRELEVANT_DATA]))
+
+    assert acs_enforce_request('access_script', repr([FUSION_TASK, PUBLIC_SCRIPT]))
+    assert not acs_enforce_request('access_script', repr([FUSION_TASK, FUSION_TASK_SCRIPT]))
+    
+    acs_announce_fact('task_participant', repr([FUSION_TASK, FUSION_TASK_SCRIPT_WRITER]))
+    assert acs_enforce_request('access_script', repr([FUSION_TASK, FUSION_TASK_SCRIPT]))
+
+    acs_announce_fact('task_participant', repr([FUSION_TASK, IRRELEVANT_PARTY]))
+    assert acs_enforce_request('access_script', repr([FUSION_TASK, FUSION_TASK_SCRIPT]))
+
+    acs_announce_fact('task_creator', repr([IRRELEVANT_TASK, IRRELEVANT_PARTY]))
+    acs_announce_fact('task_participant', repr([IRRELEVANT_TASK, IRRELEVANT_PARTY]))
+    acs_announce_fact('task_participant', repr([IRRELEVANT_TASK, FUSION_TASK_PARTY_2]))
+    
+    assert acs_enforce_request('launch_task', repr([IRRELEVANT_TASK, set([IRRELEVANT_PARTY, FUSION_TASK_PARTY_2])]))
+    assert not acs_enforce_request('access_data', repr([IRRELEVANT_TASK, FUSION_TASK_DATA_1]))
+    assert acs_enforce_request('access_data', repr([IRRELEVANT_TASK, FUSION_TASK_DATA_2]))
+    assert not acs_enforce_request('access_script', repr([IRRELEVANT_TASK, FUSION_TASK_SCRIPT]))
+    assert acs_enforce_request('access_script', repr([IRRELEVANT_TASK, PUBLIC_SCRIPT]))
+
+    assert acs_enforce_request('delete_data', repr([FUSION_TASK_PARTY_1, FUSION_TASK_DATA_1]))
+    assert not acs_enforce_request('delete_data', repr([FUSION_TASK_PARTY_1, FUSION_TASK_DATA_2]))
+    assert not acs_enforce_request('delete_data', repr([FUSION_TASK_PARTY_1, IRRELEVANT_DATA]))
+    assert not acs_enforce_request('delete_script', repr([FUSION_TASK_PARTY_1, FUSION_TASK_SCRIPT]))
+    assert not acs_enforce_request('delete_script', repr([FUSION_TASK_PARTY_1, PUBLIC_SCRIPT]))
+
+    assert not acs_enforce_request('delete_data', repr([FUSION_TASK_PARTY_2, FUSION_TASK_DATA_1]))
+    assert acs_enforce_request('delete_data', repr([FUSION_TASK_PARTY_2, FUSION_TASK_DATA_2]))
+    assert not acs_enforce_request('delete_data', repr([FUSION_TASK_PARTY_2, IRRELEVANT_DATA]))
+    assert not acs_enforce_request('delete_script', repr([FUSION_TASK_PARTY_2, FUSION_TASK_SCRIPT]))
+    assert not acs_enforce_request('delete_script', repr([FUSION_TASK_PARTY_2, PUBLIC_SCRIPT]))
+
+    assert not acs_enforce_request('delete_data', repr([FUSION_TASK_SCRIPT_WRITER, FUSION_TASK_DATA_1]))
+    assert not acs_enforce_request('delete_data', repr([FUSION_TASK_SCRIPT_WRITER, FUSION_TASK_DATA_2]))
+    assert not acs_enforce_request('delete_data', repr([FUSION_TASK_SCRIPT_WRITER, IRRELEVANT_DATA]))
+    assert acs_enforce_request('delete_script', repr([FUSION_TASK_SCRIPT_WRITER, FUSION_TASK_SCRIPT]))
+    assert not acs_enforce_request('delete_script', repr([FUSION_TASK_SCRIPT_WRITER, PUBLIC_SCRIPT]))
diff --git a/mesatee_services/acs/python/ffi.py b/mesatee_services/acs/python/ffi.py
index 330f4a2..3f56be7 100644
--- a/mesatee_services/acs/python/ffi.py
+++ b/mesatee_services/acs/python/ffi.py
@@ -4,8 +4,12 @@ import _cffi_backend as backend
 
 ffi = sgx_cffi.FFI(backend)
 
-ffi.embedding_api("void mesapy_setup_model(const char *);")
-with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "perm.py")) as f:
+ffi.embedding_api("int acs_setup_model(const char *configuration);")
+ffi.embedding_api("""int acs_enforce_request(const char *request_type, 
+                                             const char *request_content);""")
+ffi.embedding_api("""int acs_announce_fact(const char *term_type, 
+                                           const char *term_fact);""")
+with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "acs_engine.py")) as f:
     ffi.embedding_init_code(f.read())
 ffi.set_source('acs_py_enclave', '')
 ffi.emit_c_code(os.environ.get('PYPY_FFI_OUTDIR', ".") + "/acs_py_enclave.c")
diff --git a/mesatee_services/acs/sgx_trusted_lib/src/acs.rs b/mesatee_services/acs/sgx_trusted_lib/src/acs.rs
index b59e2cb..4e2c735 100644
--- a/mesatee_services/acs/sgx_trusted_lib/src/acs.rs
+++ b/mesatee_services/acs/sgx_trusted_lib/src/acs.rs
@@ -15,101 +15,217 @@
 // Insert std prelude in the top for the sgx feature
 #[cfg(feature = "mesalock_sgx")]
 use std::prelude::v1::*;
-use std::vec;
 
-use uuid::Uuid;
-
-use mesatee_core::db::Memdb;
 use mesatee_core::rpc::EnclaveService;
-use mesatee_core::{Error, ErrorKind, Result};
-use std::marker::PhantomData;
-
-use acs_proto::AEADKeyConfig;
-use acs_proto::KMSResponse;
-use acs_proto::{CreateKeyRequest, DeleteKeyRequest, GetKeyRequest, KMSRequest};
-
-use lazy_static::lazy_static;
-
-lazy_static! {
-    static ref KEY_STORE: Memdb<String, AEADKeyConfig> = {
-        let db = Memdb::<String, AEADKeyConfig>::open().expect("cannot open memdb");
-        let fake_record = AEADKeyConfig {
-            key: vec![65; 32],
-            nonce: vec![65; 12],
-            ad: vec![65; 5],
-        };
-        let _ = db.set(&"fake_kms_record".to_string(), &fake_record);
-        let _ = db.set(&"fake_kms_record_to_be_deleted".to_string(), &fake_record);
-        db
-    };
+use mesatee_core::{Result, ErrorKind};
+use std::ffi::CString;
+use std::os::raw::c_char;
+use std::collections::HashSet;
+
+use acs_proto::*;
+
+pub trait PyMarshallable {
+    fn marshal(&self, buffer: &mut String);
 }
 
-pub trait HandleRequest {
-    fn handle_request(&self) -> Result<KMSResponse>;
+impl<T> PyMarshallable for (T,)
+where
+    T: PyMarshallable,
+{
+    fn marshal(&self, buffer: &mut String) {
+        buffer.push('[');
+        self.0.marshal(buffer);
+        buffer.push(']');
+    }
+}
+
+impl<U, V> PyMarshallable for (U, V)
+where
+    U: PyMarshallable,
+    V: PyMarshallable,
+{
+    fn marshal(&self, buffer: &mut String) {
+        buffer.push('[');
+        self.0.marshal(buffer);
+        buffer.push(',');
+        self.1.marshal(buffer);
+        buffer.push(']');
+    }
 }
 
-impl HandleRequest for CreateKeyRequest {
-    fn handle_request(&self) -> Result<KMSResponse> {
-        let key_config = AEADKeyConfig::new()?;
-        let key_id = Uuid::new_v4().to_string();
-        if KEY_STORE.get(&key_id)?.is_some() {
-            return Err(Error::from(ErrorKind::UUIDError));
+impl<X, Y, Z> PyMarshallable for (X, Y, Z)
+where
+    X: PyMarshallable,
+    Y: PyMarshallable,
+    Z: PyMarshallable,
+{
+    fn marshal(&self, buffer: &mut String) {
+        buffer.push('[');
+        self.0.marshal(buffer);
+        buffer.push(',');
+        self.1.marshal(buffer);
+        buffer.push(',');
+        self.2.marshal(buffer);
+        buffer.push(']');
+    }
+}
+
+impl<T> PyMarshallable for [T] where T: PyMarshallable {
+    fn marshal(&self, buffer: &mut String) {
+        buffer.push('[');
+        for t in self.as_ref() {
+            t.marshal(buffer);
+            buffer.push(',');
         }
-        KEY_STORE.set(&key_id, &key_config)?;
-        let resp = KMSResponse::new_create_key(&key_id, &key_config);
-        Ok(resp)
+        buffer.push(']');
     }
 }
 
-impl HandleRequest for GetKeyRequest {
-    fn handle_request(&self) -> Result<KMSResponse> {
-        let key_config = KEY_STORE
-            .get(&self.key_id)?
-            .ok_or_else(|| Error::from(ErrorKind::MissingValue))?;
+impl<T: PyMarshallable> PyMarshallable for &HashSet<T> {
+    fn marshal(&self, buffer: &mut String) {
+        buffer.push_str("set([");
+        for t in *self {
+            t.marshal(buffer);
+            buffer.push(',');
+        }
+        buffer.push_str("])");
+    }
+}
 
-        let resp = KMSResponse::new_get_key(&key_config);
-        Ok(resp)
+impl PyMarshallable for String {
+    fn marshal(&self, buffer: &mut String) {
+        buffer.push('\'');
+        buffer.push_str(self);
+        buffer.push('\'');
     }
 }
 
-impl HandleRequest for DeleteKeyRequest {
-    fn handle_request(&self) -> Result<KMSResponse> {
-        let key_config = KEY_STORE
-            .del(&self.key_id)?
-            .ok_or_else(|| Error::from(ErrorKind::MissingValue))?;
+impl PyMarshallable for &String {
+    fn marshal(&self, buffer: &mut String) {
+        buffer.push('\'');
+        buffer.push_str(self);
+        buffer.push('\'');
+    }
+}
 
-        let resp = KMSResponse::new_del_key(&key_config);
-        Ok(resp)
+pub trait HandleRequest {
+    fn handle_request(&self) -> Result<ACSResponse>;
+}
+
+extern "C" {
+    fn acs_enforce_request(request_type: *const c_char, request_content: *const c_char) -> i32;
+    fn acs_announce_fact(fact_type: *const c_char, fact_vals: *const c_char) -> i32;
+}
+
+impl HandleRequest for EnforceRequest {
+    fn handle_request(&self) -> Result<ACSResponse> {
+        let (request_type, request_content) = match self {
+            EnforceRequest::LaunchTask(usr, task) => {
+                let mut buffer = String::new();
+                (usr, task).marshal(&mut buffer);
+                ("launch_task", buffer)
+            }
+            EnforceRequest::AccessData(task, data) => {
+                let mut buffer = String::new();
+                (task, data).marshal(&mut buffer);
+                ("access_data", buffer)
+            }
+            EnforceRequest::DeleteData(usr, data) => {
+                let mut buffer = String::new();
+                (usr, data).marshal(&mut buffer);
+                ("delete_data", buffer)
+            }
+            EnforceRequest::AccessScript(task, script) => {
+                let mut buffer = String::new();
+                (task, script).marshal(&mut buffer);
+                ("access_script", buffer)
+            }
+            EnforceRequest::DeleteScript(usr, script) => {
+                let mut buffer = String::new();
+                (usr, script).marshal(&mut buffer);
+                ("delete_script", buffer)
+            }
+        };
+        
+        let c_request_type = CString::new(request_type.to_string()).unwrap();
+        let c_request_content = CString::new(request_content).unwrap();
+        let py_ret = unsafe {
+            acs_enforce_request(c_request_type.as_ptr(), c_request_content.as_ptr())
+        };
+
+        match py_ret {
+            0 => Ok(ACSResponse::Enforce(false)),
+            1 => Ok(ACSResponse::Enforce(true)),
+            _ => Err(ErrorKind::MesaPyError.into()),
+        }
     }
 }
 
-pub struct KMSEnclave<S, T> {
-    state: i32,
-    x: PhantomData<S>,
-    y: PhantomData<T>,
+impl HandleRequest for AnnounceRequest {
+    fn handle_request(&self) -> Result<ACSResponse> {
+        for fact in &self.facts {
+            use AccessControlTerms::*;
+            let (term_type, term_fact) = match fact {
+                TaskCreator(task, usr) => {
+                    let mut buffer = String::new();
+                    (task, usr).marshal(&mut buffer);
+                    ("task_creator", buffer)
+                }
+                TaskParticipant(task, usr) => {
+                    let mut buffer = String::new();
+                    (task, usr).marshal(&mut buffer);
+                    ("task_participant", buffer)
+                }
+                DataOwner(data, usr) => {
+                    let mut buffer = String::new();
+                    (data, usr).marshal(&mut buffer);
+                    ("data_owner", buffer)
+                }
+                ScriptOwner(script, usr) => {
+                    let mut buffer = String::new();
+                    (script, usr).marshal(&mut buffer);
+                    ("script_owner", buffer)
+                }
+                IsPublicScript(script) => {
+                    let mut buffer = String::new();
+                    (script,).marshal(&mut buffer);
+                    ("is_public_script", buffer)
+                }
+            };
+
+            let c_term_type = CString::new(term_type.to_string()).unwrap();
+            let c_term_fact = CString::new(term_fact).unwrap();
+
+            let py_ret = unsafe {
+                acs_announce_fact(c_term_type.as_ptr(), c_term_fact.as_ptr())
+            };
+            
+            if py_ret != 0 {
+                return Err(ErrorKind::MesaPyError.into());
+            }
+        }
+        Ok(ACSResponse::Announce)
+    }
 }
 
-impl<S, T> Default for KMSEnclave<S, T> {
+pub struct ACSEnclave;
+
+impl Default for ACSEnclave {
     fn default() -> Self {
-        KMSEnclave {
-            state: 0,
-            x: PhantomData::<S>,
-            y: PhantomData::<T>,
-        }
+        ACSEnclave {}
     }
 }
 
-impl EnclaveService<KMSRequest, KMSResponse> for KMSEnclave<KMSRequest, KMSResponse> {
-    fn handle_invoke(&mut self, input: KMSRequest) -> Result<KMSResponse> {
-        trace!("handle_invoke invoked!");
-        trace!("incoming payload = {:?}", input);
-        self.state += 1;
+impl EnclaveService<ACSRequest, ACSResponse> for ACSEnclave {
+    fn handle_invoke(&mut self, input: ACSRequest) -> Result<ACSResponse> {
+        debug!("handle_invoke invoked!");
+        debug!("incoming payload = {:?}", input);
+
         let response = match input {
-            KMSRequest::Create(req) => req.handle_request()?,
-            KMSRequest::Get(req) => req.handle_request()?,
-            KMSRequest::Delete(req) => req.handle_request()?,
+            ACSRequest::Enforce(req) => req.handle_request()?,
+            ACSRequest::Announce(req) => req.handle_request()?,
         };
-        trace!("{}th round complete!", self.state);
+ 
         Ok(response)
     }
 }
diff --git a/mesatee_services/acs/sgx_trusted_lib/src/sgx.rs b/mesatee_services/acs/sgx_trusted_lib/src/sgx.rs
index e4f1a4a..85266e6 100644
--- a/mesatee_services/acs/sgx_trusted_lib/src/sgx.rs
+++ b/mesatee_services/acs/sgx_trusted_lib/src/sgx.rs
@@ -21,12 +21,12 @@ use std::os::raw::c_char;
 
 use mesatee_core::config;
 use mesatee_core::prelude::*;
-use mesatee_core::Result;
+use mesatee_core::{Result, Error, ErrorKind};
 
 use env_logger;
 use std::backtrace::{self, PrintFormat};
 
-use crate::acs::KMSEnclave;
+use crate::acs::ACSEnclave;
 
 register_ecall_handler!(
     type ECallCommand,
@@ -36,18 +36,18 @@ register_ecall_handler!(
 );
 
 extern "C" {
-    fn mesapy_setup_model(model_text: *const c_char);
+    fn acs_setup_model(model_text: *const c_char) -> i32;
 }
 
 #[handle_ecall]
 fn handle_serve_connection(args: &ServeConnectionInput) -> Result<ServeConnectionOutput> {
-    debug!("Enclave [KMS]: Serve Connection.");
+    debug!("Enclave [ACS]: Serve Connection.");
 
-    let server_instance = KMSEnclave::default();
-    let kms_config = config::Internal::kms();
-    assert_eq!(args.port, kms_config.addr.port());
+    let server_instance = ACSEnclave::default();
+    let acs_config = config::Internal::acs();
+    assert_eq!(args.port, acs_config.addr.port());
 
-    let enclave_attr = match kms_config.inbound_desc {
+    let enclave_attr = match acs_config.inbound_desc {
         config::InboundDesc::Sgx(enclave_attr) => enclave_attr,
         _ => unreachable!(),
     };
@@ -76,7 +76,7 @@ const MODEL_TEXT: &'static str = include_str!("../../model.conf");
 
 #[handle_ecall]
 fn handle_init_enclave(_args: &InitEnclaveInput) -> Result<InitEnclaveOutput> {
-    debug!("Enclave [KMS]: Initializing...");
+    debug!("Enclave [ACS]: Initializing...");
 
     env_logger::init();
     let _ = backtrace::enable_backtrace(
@@ -85,11 +85,15 @@ fn handle_init_enclave(_args: &InitEnclaveInput) -> Result<InitEnclaveOutput> {
     );
     mesatee_core::rpc::sgx::prelude();
 
-    unsafe {
-        mesapy_setup_model(CString::new(MODEL_TEXT).unwrap().as_ptr());
-    }
+    eprintln!("setting up acs model");
+
+    let ec = unsafe { acs_setup_model(CString::new(MODEL_TEXT).unwrap().as_ptr()) };
 
-    Ok(InitEnclaveOutput::default())
+    if ec != 0 {
+        Err(Error::from(ErrorKind::MesaPyError))
+    } else {
+        Ok(InitEnclaveOutput::default())
+    }
 }
 
 #[handle_ecall]
@@ -97,6 +101,6 @@ fn handle_finalize_enclave(_args: &FinalizeEnclaveInput) -> Result<FinalizeEncla
     #[cfg(feature = "cov")]
     sgx_cov::cov_writeout();
 
-    debug!("Enclave [KMS]: Finalized.");
+    debug!("Enclave [ACS]: Finalized.");
     Ok(FinalizeEnclaveOutput::default())
 }
diff --git a/tests/functional_test.sh b/tests/functional_test.sh
index a825c23..963c60f 100755
--- a/tests/functional_test.sh
+++ b/tests/functional_test.sh
@@ -15,6 +15,10 @@ if lsof -i :5066; then
     echo "[-] port 5066 is in use"
     exit 1
 fi
+if lsof -i :5077; then
+    echo "[-] port 5066 is in use"
+    exit 1
+fi
 if lsof -i :5065; then
     echo "[-] port 5065 is in use"
     exit 1
@@ -27,10 +31,6 @@ if lsof -i :5555; then
     echo "[-] port 5555 is in use"
     exit 1
 fi
-if lsof -i :5555; then
-    echo "[-] port 5555 is in use"
-    exit 1
-fi
 if lsof -i :3444; then
     echo "[-] port 3444 is in use"
     exit 1
@@ -41,6 +41,7 @@ fi
 ./tdfs 2>&1 | tee tdfs.log &
 ./tms 2>&1 | tee tms.log &
 ./fns 2>&1 | tee fns.log &
+./acs 2>&1 | tee acs.log &
 
 wait_service() {
     name=$1
@@ -61,6 +62,7 @@ wait_service tdfs 5065 30
 wait_service tms 5554 30
 wait_service tms 5555 30
 wait_service fns 3444 30
+wait_service acs 5077 30
 
 ./functional_test 2>&1 | tee functional_test.log
 exit ${PIPESTATUS[0]}
diff --git a/tests/functional_test/sgx_trusted_lib/Cargo.toml b/tests/functional_test/sgx_trusted_lib/Cargo.toml
index 24de3aa..866a392 100644
--- a/tests/functional_test/sgx_trusted_lib/Cargo.toml
+++ b/tests/functional_test/sgx_trusted_lib/Cargo.toml
@@ -12,7 +12,7 @@ crate-type = ["staticlib"]
 
 [features]
 default = []
-mesalock_sgx = ["sgx_tstd", "sgx_tunittest", "mesatee_core/mesalock_sgx", "kms_client/mesalock_sgx", "tdfs_internal_client/mesalock_sgx", "tms_internal_client/mesalock_sgx", "tms_internal_proto/mesalock_sgx", "protected_fs_rs/mesalock_sgx"]
+mesalock_sgx = ["sgx_tstd", "sgx_tunittest", "mesatee_core/mesalock_sgx", "kms_client/mesalock_sgx", "acs_client/mesalock_sgx", "tdfs_internal_client/mesalock_sgx", "tms_internal_client/mesalock_sgx", "tms_internal_proto/mesalock_sgx", "protected_fs_rs/mesalock_sgx"]
 cov = ["sgx_cov"]
 
 [dependencies]
@@ -22,6 +22,7 @@ env_logger      = { version = "0.6.1" }
 rand            = { version = "0.7" }
 
 kms_client      = { path = "../../../mesatee_services/kms/client", optional = true }
+acs_client      = { path = "../../../mesatee_services/acs/client", optional = true }
 tdfs_internal_client = { path = "../../../mesatee_services/tdfs/internal/client", optional = true }
 tms_internal_client = { path = "../../../mesatee_services/tms/internal/client", optional = true }
 tms_internal_proto = { path = "../../../mesatee_services/tms/internal/proto", optional = true }
diff --git a/tests/functional_test/sgx_trusted_lib/src/sgx.rs b/tests/functional_test/sgx_trusted_lib/src/sgx.rs
index 1fc2faa..3069e7e 100644
--- a/tests/functional_test/sgx_trusted_lib/src/sgx.rs
+++ b/tests/functional_test/sgx_trusted_lib/src/sgx.rs
@@ -47,6 +47,7 @@ fn handle_run_functional_test(_args: &RunFunctionalTestInput) -> Result<RunFunct
         tests::tms_test::update_task_result,
         tests::tms_test::update_private_result,
         tests::tms_test::update_status,
+        tests::acs_test::access_control_model,
     );
 
     Ok(RunFunctionalTestOutput::new(nfailed))
diff --git a/tests/functional_test/sgx_trusted_lib/src/tests/acs_test.rs b/tests/functional_test/sgx_trusted_lib/src/tests/acs_test.rs
new file mode 100644
index 0000000..51d2d5c
--- /dev/null
+++ b/tests/functional_test/sgx_trusted_lib/src/tests/acs_test.rs
@@ -0,0 +1,267 @@
+// Copyright 2019 MesaTEE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.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.
+
+use super::common_setup::setup_acs_internal_client;
+
+use std::collections::HashSet;
+use std::string::ToString;
+    
+const FUSION_TASK: &'static str = "data_fusion";
+
+const FUSION_TASK_PARTY_1: &'static str = "usr1";
+const FUSION_TASK_PARTY_2: &'static str = "usr2";
+
+const FUSION_TASK_DATA_1: &'static str = "data1";
+const FUSION_TASK_DATA_2: &'static str = "data2";
+
+const FUSION_TASK_SCRIPT: &'static str = "fusion_script";
+const FUSION_TASK_SCRIPT_WRITER: &'static str = "usr3";
+const PUBLIC_SCRIPT: &'static str = "public_script";
+const PUBLIC_SCRIPT_WRITER: &'static str = "usr4";
+
+const IRRELEVANT_TASK: &'static str = "task_irrelevant";
+const IRRELEVANT_PARTY: &'static str = "usr_irrelevant";
+const IRRELEVANT_DATA: &'static str = "data_irrelevant";
+
+pub fn access_control_model() {
+    trace!("Test ACS: access control model.");
+    let mut client = setup_acs_internal_client();
+
+    let mut participants = HashSet::new();
+    participants.insert(FUSION_TASK_PARTY_1.to_string());
+    participants.insert(FUSION_TASK_PARTY_2.to_string());
+    participants.insert(FUSION_TASK_SCRIPT_WRITER.to_string());
+
+    client.announce_task_creation(
+        FUSION_TASK.to_string(),
+        FUSION_TASK_PARTY_1.to_string(),
+        &participants,
+    ).expect("fusion task creation announcement failed");
+
+    client.announce_data_creation(
+        FUSION_TASK_DATA_1.to_string(),
+        FUSION_TASK_PARTY_1.to_string(),
+    ).expect("fusion data n1 creation announcement failed");
+
+    client.announce_data_creation(
+        FUSION_TASK_DATA_2.to_string(),
+        FUSION_TASK_PARTY_2.to_string(),
+    ).expect("fusion data 2 creation announcement failed");
+
+    client.announce_data_creation(
+        IRRELEVANT_DATA.to_string(),
+        IRRELEVANT_PARTY.to_string(),
+    ).expect("irrelevant data creation announcement failed");
+
+    client.announce_script_creation(
+        FUSION_TASK_SCRIPT.to_string(),
+        FUSION_TASK_SCRIPT_WRITER.to_string(),
+        false,
+    ).expect("fusion script creation announcement failed");
+
+    client.announce_script_creation(
+        PUBLIC_SCRIPT.to_string(),
+        PUBLIC_SCRIPT_WRITER.to_string(),
+        true,
+    ).expect("public script creation announcement failed");
+
+    let mut participants = HashSet::new();
+    
+    assert_eq!(
+        client.enforce_task_launch(
+            FUSION_TASK.to_string(),
+            participants.clone(),
+        ).unwrap(),
+        false,
+    );
+
+    participants.insert(FUSION_TASK_PARTY_1.to_string());
+
+    assert_eq!(
+        client.enforce_task_launch(
+            FUSION_TASK.to_string(),
+            participants.clone(),
+        ).unwrap(),
+        false,
+    );
+
+    participants.insert(FUSION_TASK_PARTY_2.to_string());
+
+        assert_eq!(
+        client.enforce_task_launch(
+            FUSION_TASK.to_string(),
+            participants.clone(),
+        ).unwrap(),
+        false,
+    );
+
+    participants.insert(FUSION_TASK_SCRIPT_WRITER.to_string());
+
+    assert_eq!(
+        client.enforce_task_launch(
+            FUSION_TASK.to_string(),
+            participants.clone(),
+        ).unwrap(),
+        true,
+    );
+    
+    // Load fusion script
+    assert_eq!(
+        client.enforce_script_access(
+            FUSION_TASK.to_string(),
+            FUSION_TASK_SCRIPT.to_string(),
+        ).unwrap(),
+        true,
+    );
+
+    // Load public script
+    assert_eq!(
+        client.enforce_script_access(
+            FUSION_TASK.to_string(),
+            PUBLIC_SCRIPT.to_string(),
+        ).unwrap(),
+        true,
+    );
+
+    // Read data1
+    assert_eq!(
+        client.enforce_data_access(
+            FUSION_TASK.to_string(),
+            FUSION_TASK_DATA_1.to_string(),
+        ).unwrap(),
+        true,
+    );
+
+    // Read data2
+    assert_eq!(
+        client.enforce_data_access(
+            FUSION_TASK.to_string(),
+            FUSION_TASK_DATA_2.to_string(),
+        ).unwrap(),
+        true,
+    );
+
+    let mut participants = HashSet::new();
+
+    participants.insert(IRRELEVANT_PARTY.to_string());
+    participants.insert(FUSION_TASK_PARTY_2.to_string());
+    
+    client.announce_task_creation(
+        IRRELEVANT_TASK.to_string(),
+        IRRELEVANT_PARTY.to_string(),
+        &participants,
+    ).expect("irrelevant task creation announcement failed");
+
+    // Launch irrelevant task
+    assert_eq!(
+        client.enforce_task_launch(
+            IRRELEVANT_TASK.to_string(),
+            participants,
+        ).unwrap(),
+        true,
+    );
+
+    // Load fusion script; deny
+    assert_eq!(
+        client.enforce_script_access(
+            IRRELEVANT_TASK.to_string(),
+            FUSION_TASK_SCRIPT.to_string(),
+        ).unwrap(),
+        false,
+    );
+
+    // Load public script; allow
+    assert_eq!(
+        client.enforce_script_access(
+            IRRELEVANT_TASK.to_string(),
+            PUBLIC_SCRIPT.to_string(),
+        ).unwrap(),
+        true,
+    );
+
+    // Read data1; deny
+    assert_eq!(
+        client.enforce_data_access(
+            IRRELEVANT_TASK.to_string(),
+            FUSION_TASK_DATA_1.to_string(),
+        ).unwrap(),
+        false,
+    );
+
+    // Read data2; allow
+    assert_eq!(
+        client.enforce_data_access(
+            IRRELEVANT_TASK.to_string(),
+            FUSION_TASK_DATA_2.to_string(),
+        ).unwrap(),
+        true,
+    );
+
+    assert_eq!(
+        client.enforce_data_deletion(
+            FUSION_TASK_PARTY_1.to_string(),
+            FUSION_TASK_DATA_1.to_string(),
+        ).unwrap(),
+        true,
+    );
+
+    assert_eq!(
+        client.enforce_data_deletion(
+            FUSION_TASK_PARTY_2.to_string(),
+            FUSION_TASK_DATA_2.to_string(),
+        ).unwrap(),
+        true,
+    );
+
+    assert_eq!(
+        client.enforce_data_deletion(
+            FUSION_TASK_PARTY_1.to_string(),
+            FUSION_TASK_DATA_2.to_string(),
+        ).unwrap(),
+        false,
+    );
+
+    assert_eq!(
+        client.enforce_script_deletion(
+            FUSION_TASK_PARTY_1.to_string(),
+            FUSION_TASK_SCRIPT.to_string(),
+        ).unwrap(),
+        false,
+    );
+
+    assert_eq!(
+        client.enforce_script_deletion(
+            FUSION_TASK_SCRIPT_WRITER.to_string(),
+            FUSION_TASK_SCRIPT.to_string(),
+        ).unwrap(),
+        true,
+    );
+
+    assert_eq!(
+        client.enforce_script_deletion(
+            IRRELEVANT_PARTY.to_string(),
+            PUBLIC_SCRIPT.to_string(),
+        ).unwrap(),
+        false,
+    );
+
+    assert_eq!(
+        client.enforce_script_deletion(
+            PUBLIC_SCRIPT_WRITER.to_string(),
+            PUBLIC_SCRIPT.to_string(),
+        ).unwrap(),
+        true,
+    );
+
+}
diff --git a/tests/functional_test/sgx_trusted_lib/src/tests/common_setup.rs b/tests/functional_test/sgx_trusted_lib/src/tests/common_setup.rs
index b6fa952..92244f3 100644
--- a/tests/functional_test/sgx_trusted_lib/src/tests/common_setup.rs
+++ b/tests/functional_test/sgx_trusted_lib/src/tests/common_setup.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 use kms_client::KMSClient;
+use acs_client::ACSClient;
 use mesatee_core::config;
 use tdfs_internal_client::TDFSClient;
 use tms_internal_client::TMSClient;
@@ -21,6 +22,11 @@ pub(crate) fn setup_kms_internal_client() -> KMSClient {
     KMSClient::new(target).unwrap()
 }
 
+pub(crate) fn setup_acs_internal_client() -> ACSClient {
+    let target = config::Internal::target_acs();
+    ACSClient::new(target).unwrap()
+}
+
 pub(crate) fn setup_tdfs_internal_client() -> TDFSClient {
     let target = config::Internal::target_tdfs();
     TDFSClient::new(target).unwrap()
@@ -30,3 +36,4 @@ pub(crate) fn setup_tms_internal_client() -> TMSClient {
     let target = config::Internal::target_tms();
     TMSClient::new(target).unwrap()
 }
+
diff --git a/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs b/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs
index dde9bb0..d8b9735 100644
--- a/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs
+++ b/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs
@@ -17,3 +17,4 @@ pub mod kms_test;
 pub mod protected_fs_test;
 pub mod tdfs_test;
 pub mod tms_test;
+pub mod acs_test;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@mesatee.apache.org
For additional commands, e-mail: commits-help@mesatee.apache.org