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

mssun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-teaclave.git


The following commit(s) were added to refs/heads/master by this push:
     new d670e6a  Add Rust client SDK (#455)
d670e6a is described below

commit d670e6a1b2d1390af573fef11ce6931e0b3fee16
Author: Mingshen Sun <[email protected]>
AuthorDate: Mon Jan 18 22:22:41 2021 -0800

    Add Rust client SDK (#455)
---
 attestation/Cargo.toml                          |   3 +-
 cmake/TeaclaveGenVars.cmake                     |   1 +
 cmake/UtilTargets.cmake                         |   3 +
 cmake/scripts/test.sh                           |  42 ++
 docs/build-system.md                            |   1 +
 sdk/rust/Cargo.toml                             |  16 +
 sdk/rust/build.rs                               |  20 +
 sdk/rust/src/lib.rs                             | 601 ++++++++++++++++++++++++
 services/proto/build.rs                         |  18 +-
 services/proto/proto_gen/templates/proto.j2     |   4 +
 services/proto/src/teaclave_frontend_service.rs |   7 +-
 types/Cargo.toml                                |   1 +
 types/src/attestation.rs                        |   6 +
 types/src/crypto.rs                             |   7 +
 14 files changed, 720 insertions(+), 10 deletions(-)

diff --git a/attestation/Cargo.toml b/attestation/Cargo.toml
index 9f1c37f..75a0d46 100644
--- a/attestation/Cargo.toml
+++ b/attestation/Cargo.toml
@@ -15,6 +15,7 @@ mesalock_sgx = [
     "sgx_tse",
     "teaclave_types/mesalock_sgx",
     "teaclave_config/mesalock_sgx",
+    "teaclave_config/build_config",
 ]
 enclave_unit_test = ["teaclave_test_utils/mesalock_sgx"]
 
@@ -40,7 +41,7 @@ webpki-roots     = { version = "0.19.0" }
 yasna            = { version = "0.3.0", features = ["bit-vec", "num-bigint", 
"chrono"] }
 
 teaclave_types  = { path = "../types" }
-teaclave_config = { path = "../config", features = ["build_config"] }
+teaclave_config = { path = "../config" }
 teaclave_test_utils = { path = "../tests/utils", optional = true }
 
 sgx_rand    = { version = "1.1.2", optional = true }
diff --git a/cmake/TeaclaveGenVars.cmake b/cmake/TeaclaveGenVars.cmake
index 1751356..8a00491 100644
--- a/cmake/TeaclaveGenVars.cmake
+++ b/cmake/TeaclaveGenVars.cmake
@@ -132,6 +132,7 @@ set(TEACLAVE_COMMON_ENVS
     TEACLAVE_BUILD_CFG_DIR=${PROJECT_SOURCE_DIR}
     TEACLAVE_EDL_DIR=${TEACLAVE_EDL_DIR}
     TEACLAVE_SYMLINKS=${TEACLAVE_SYMLINKS}
+    RUSTFLAGS=${RUSTFLAGS}
     SGX_SDK=${SGX_SDK}
     SGX_MODE=${SGX_MODE}
     DCAP=${DCAP}
diff --git a/cmake/UtilTargets.cmake b/cmake/UtilTargets.cmake
index 9614017..de388a6 100644
--- a/cmake/UtilTargets.cmake
+++ b/cmake/UtilTargets.cmake
@@ -70,6 +70,9 @@ if(TEST_MODE)
   add_custom_target(
     run-functional-tests COMMAND ${TEACLAVE_COMMON_ENVS}
                                  ${MT_SCRIPT_DIR}/test.sh functional)
+  add_custom_target(
+    run-sdk-tests COMMAND ${TEACLAVE_COMMON_ENVS}
+                          ${MT_SCRIPT_DIR}/test.sh sdk)
 else()
   add_custom_target(
     run-tests
diff --git a/cmake/scripts/test.sh b/cmake/scripts/test.sh
index ab3a5df..6702253 100755
--- a/cmake/scripts/test.sh
+++ b/cmake/scripts/test.sh
@@ -151,6 +151,44 @@ run_functional_tests() {
   cleanup
 }
 
+run_sdk_tests() {
+  trap cleanup INT TERM ERR
+
+  echo_title "SDK tests"
+  mkdir -p /tmp/fusion_data
+  pushd ${TEACLAVE_CLI_INSTALL_DIR}
+  ./teaclave_cli verify \
+                 --enclave-info ../examples/enclave_info.toml \
+                 --public-keys $(find ../examples -name "*.public.pem") \
+                 --signatures $(find ../examples -name "*.sign.sha256")
+  popd
+  pushd ${TEACLAVE_SERVICE_INSTALL_DIR}
+  ./teaclave_authentication_service &
+  ./teaclave_storage_service &
+  sleep 3    # wait for authentication and storage service
+  ./teaclave_management_service &
+  ./teaclave_scheduler_service &
+  sleep 3    # wait for management service and scheduler_service
+  ./teaclave_access_control_service &
+  ./teaclave_frontend_service &
+  sleep 3    # wait for other services
+
+  start_storage_server
+
+  # Run tests of execution service separately
+  ./teaclave_execution_service &
+  sleep 3    # wait for execution services
+  popd
+
+  pushd ${MT_SGXAPP_TOML_DIR}
+  RUSTFLAGS=${RUSTFLAGS} cargo test --manifest-path 
${TEACLAVE_PROJECT_ROOT}/sdk/rust/Cargo.toml \
+        --target-dir ${TEACLAVE_TARGET_DIR}/untrusted
+  popd
+
+  # kill all background services
+  cleanup
+}
+
 run_examples() {
   trap cleanup INT TERM ERR
 
@@ -208,6 +246,9 @@ case "$1" in
     "functional")
         run_functional_tests
         ;;
+    "sdk")
+        run_sdk_tests
+        ;;
     "example")
         run_examples
         ;;
@@ -215,6 +256,7 @@ case "$1" in
         run_unit_tests
         run_integration_tests
         run_functional_tests
+        run_sdk_tests
         run_examples
         ;;
 esac
diff --git a/docs/build-system.md b/docs/build-system.md
index 025cdae..23060be 100644
--- a/docs/build-system.md
+++ b/docs/build-system.md
@@ -117,6 +117,7 @@ Above targets are automatically generated from the
 - `run-tests`: Run all test cases.
 - `run-integration-tests`: Run integration tests only.
 - `run-funtional-tests`: Run functional tests only.
+- `run-sdk-tests`: Run tests of client SDK only.
 - `run-examples`: Run all examples.
 - `cov`: Aggregate coverage results and generate report, needs to config cmake
   with `-DCOV=ON`.
diff --git a/sdk/rust/Cargo.toml b/sdk/rust/Cargo.toml
new file mode 100644
index 0000000..4264c17
--- /dev/null
+++ b/sdk/rust/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "teaclave-client-sdk"
+version = "0.1.0"
+authors = ["Teaclave Contributors <[email protected]>"]
+description = "Teaclave Rust Client SDK"
+license = "Apache-2.0"
+edition = "2018"
+
+[dependencies]
+teaclave_types = { path = "../../types", features = ["app"] }
+teaclave_attestation = { path = "../../attestation" }
+teaclave_rpc = { path = "../../rpc" }
+teaclave_proto = { path = "../../services/proto" }
+anyhow       = { version = "1.0.26" }
+url          = { version = "2.1.1" }
+pem = "0.7.0"
diff --git a/sdk/rust/build.rs b/sdk/rust/build.rs
new file mode 100644
index 0000000..49c8b00
--- /dev/null
+++ b/sdk/rust/build.rs
@@ -0,0 +1,20 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.
+
+fn main() {
+    println!("cargo:rustc-env=MT_SGXAPP_TOML_DIR=../../");
+}
diff --git a/sdk/rust/src/lib.rs b/sdk/rust/src/lib.rs
new file mode 100644
index 0000000..7357d19
--- /dev/null
+++ b/sdk/rust/src/lib.rs
@@ -0,0 +1,601 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 anyhow::Result;
+use std::collections::HashMap;
+use std::convert::TryInto;
+use teaclave_attestation::verifier;
+use 
teaclave_proto::teaclave_authentication_service::TeaclaveAuthenticationApiClient;
+use teaclave_proto::teaclave_frontend_service::TeaclaveFrontendClient;
+use teaclave_rpc::config::SgxTrustedTlsClientConfig;
+use teaclave_rpc::endpoint::Endpoint;
+use teaclave_types::FileAuthTag;
+use url::Url;
+
+pub use teaclave_proto::teaclave_authentication_service::{
+    UserLoginRequest, UserLoginResponse, UserRegisterRequest, 
UserRegisterResponse,
+};
+pub use teaclave_proto::teaclave_frontend_service::GetFunctionResponse as 
Function;
+pub use teaclave_proto::teaclave_frontend_service::{
+    ApproveTaskRequest, ApproveTaskResponse, AssignDataRequest, 
AssignDataResponse,
+    CreateTaskRequest, CreateTaskResponse, GetFunctionRequest, 
GetFunctionResponse, GetTaskRequest,
+    GetTaskResponse, InvokeTaskRequest, InvokeTaskResponse, 
RegisterFunctionRequest,
+    RegisterFunctionResponse, RegisterInputFileRequest, 
RegisterInputFileResponse,
+    RegisterOutputFileRequest, RegisterOutputFileResponse,
+};
+pub use teaclave_types::{
+    EnclaveInfo, Executor, FileCrypto, FunctionInput, FunctionOutput, 
TaskResult,
+};
+
+pub struct AuthenticationClient {
+    api_client: TeaclaveAuthenticationApiClient,
+}
+
+pub struct AuthenticationService;
+
+impl AuthenticationClient {
+    pub fn new(api_client: TeaclaveAuthenticationApiClient) -> Self {
+        Self { api_client }
+    }
+
+    pub fn user_register_with_request(
+        &mut self,
+        request: UserRegisterRequest,
+    ) -> Result<UserRegisterResponse> {
+        let response = self.api_client.user_register(request)?;
+
+        Ok(response)
+    }
+
+    pub fn user_register(&mut self, user_id: &str, user_password: &str) -> 
Result<()> {
+        let request = UserRegisterRequest::new(user_id, user_password);
+        let _response = self.user_register_with_request(request)?;
+
+        Ok(())
+    }
+
+    pub fn user_login_with_request(
+        &mut self,
+        request: UserLoginRequest,
+    ) -> Result<UserLoginResponse> {
+        let response = self.api_client.user_login(request)?;
+
+        Ok(response)
+    }
+
+    pub fn user_login(&mut self, user_id: &str, user_password: &str) -> 
Result<String> {
+        let request = UserLoginRequest::new(user_id, user_password);
+        let response = self.user_login_with_request(request)?;
+
+        Ok(response.token)
+    }
+}
+
+impl AuthenticationService {
+    pub fn connect(
+        url: &str,
+        enclave_info: &EnclaveInfo,
+        as_root_ca_cert: &[u8],
+    ) -> Result<AuthenticationClient> {
+        let enclave_attr = enclave_info
+            .get_enclave_attr("teaclave_authentication_service")
+            .expect("enclave attr");
+        let config = 
SgxTrustedTlsClientConfig::new().attestation_report_verifier(
+            vec![enclave_attr],
+            as_root_ca_cert,
+            verifier::universal_quote_verifier,
+        );
+        let channel = Endpoint::new(url).config(config).connect()?;
+        let client = TeaclaveAuthenticationApiClient::new(channel)?;
+
+        Ok(AuthenticationClient::new(client))
+    }
+}
+
+pub struct FrontendService;
+
+impl FrontendService {
+    pub fn connect(
+        url: &str,
+        enclave_info: &EnclaveInfo,
+        as_root_ca_cert: &[u8],
+    ) -> Result<FrontendClient> {
+        let enclave_attr = enclave_info
+            .get_enclave_attr("teaclave_frontend_service")
+            .expect("enclave attr");
+        let config = 
SgxTrustedTlsClientConfig::new().attestation_report_verifier(
+            vec![enclave_attr],
+            as_root_ca_cert,
+            verifier::universal_quote_verifier,
+        );
+        let channel = Endpoint::new(url).config(config).connect()?;
+        let client = TeaclaveFrontendClient::new(channel)?;
+
+        Ok(FrontendClient::new(client))
+    }
+}
+
+pub struct FrontendClient {
+    api_client: TeaclaveFrontendClient,
+}
+
+impl FrontendClient {
+    pub fn new(api_client: TeaclaveFrontendClient) -> Self {
+        Self { api_client }
+    }
+
+    pub fn set_credential(&mut self, id: &str, token: &str) {
+        let mut metadata = HashMap::new();
+        metadata.insert("id".to_string(), id.to_string());
+        metadata.insert("token".to_string(), token.to_string());
+        self.api_client.set_metadata(metadata);
+    }
+
+    pub fn register_function_with_request(
+        &mut self,
+        request: RegisterFunctionRequest,
+    ) -> Result<RegisterFunctionResponse> {
+        let response = self.api_client.register_function(request)?;
+
+        Ok(response)
+    }
+
+    pub fn register_function(
+        &mut self,
+        name: &str,
+        description: &str,
+        executor_type: &str,
+        payload: Option<&[u8]>,
+        arguments: Option<&[&str]>,
+        inputs: Option<Vec<FunctionInput>>,
+        outputs: Option<Vec<FunctionOutput>>,
+    ) -> Result<String> {
+        let executor_type = executor_type.try_into()?;
+        let mut request = RegisterFunctionRequest::new()
+            .name(name)
+            .description(description)
+            .executor_type(executor_type);
+        if let Some(payload) = payload {
+            request = request.payload(payload.into());
+        }
+        if let Some(arguments) = arguments {
+            request = request.arguments(arguments);
+        }
+        if let Some(inputs) = inputs {
+            request = request.inputs(inputs);
+        }
+        if let Some(outputs) = outputs {
+            request = request.outputs(outputs);
+        }
+        let response = self.register_function_with_request(request)?;
+
+        Ok(response.function_id.to_string())
+    }
+
+    pub fn get_function_with_request(
+        &mut self,
+        request: GetFunctionRequest,
+    ) -> Result<GetFunctionResponse> {
+        let response = self.api_client.get_function(request)?;
+
+        Ok(response)
+    }
+
+    pub fn get_function(&mut self, function_id: &str) -> Result<Function> {
+        let function_id = function_id.try_into()?;
+        let request = GetFunctionRequest::new(function_id);
+        let response = self.get_function_with_request(request)?;
+
+        Ok(response)
+    }
+
+    pub fn register_input_file_with_request(
+        &mut self,
+        request: RegisterInputFileRequest,
+    ) -> Result<RegisterInputFileResponse> {
+        let response = self.api_client.register_input_file(request)?;
+
+        Ok(response)
+    }
+
+    pub fn register_input_file(
+        &mut self,
+        url: &str,
+        cmac: &[u8],
+        file_crypto: FileCrypto,
+    ) -> Result<String> {
+        let url = Url::parse(url)?;
+        let cmac = FileAuthTag::from_bytes(cmac)?;
+        let request = RegisterInputFileRequest::new(url, cmac, file_crypto);
+        let response = self.register_input_file_with_request(request)?;
+
+        Ok(response.data_id.to_string())
+    }
+
+    pub fn register_output_file_with_request(
+        &mut self,
+        request: RegisterOutputFileRequest,
+    ) -> Result<RegisterOutputFileResponse> {
+        let response = self.api_client.register_output_file(request)?;
+
+        Ok(response)
+    }
+
+    pub fn register_output_file(&mut self, url: &str, file_crypto: FileCrypto) 
-> Result<String> {
+        let url = Url::parse(url)?;
+        let request = RegisterOutputFileRequest::new(url, file_crypto);
+        let response = self.register_output_file_with_request(request)?;
+
+        Ok(response.data_id.to_string())
+    }
+
+    pub fn create_task_with_request(
+        &mut self,
+        request: CreateTaskRequest,
+    ) -> Result<CreateTaskResponse> {
+        let response = self.api_client.create_task(request)?;
+
+        Ok(response)
+    }
+
+    pub fn create_task(
+        &mut self,
+        function_id: &str,
+        function_arguments: Option<HashMap<String, String>>,
+        executor: &str,
+        inputs_ownership: Option<HashMap<String, Vec<String>>>,
+        outputs_ownership: Option<HashMap<String, Vec<String>>>,
+    ) -> Result<String> {
+        use teaclave_types::OwnerList;
+        let function_id = function_id.try_into()?;
+        let executor = executor.try_into()?;
+
+        let mut request = CreateTaskRequest::new()
+            .function_id(function_id)
+            .executor(executor);
+
+        if let Some(function_arguments) = function_arguments {
+            request = request.function_arguments(function_arguments);
+        }
+
+        if let Some(inputs_ownership) = inputs_ownership {
+            let mut inputs_task_file_owners: HashMap<String, OwnerList> = 
HashMap::new();
+            for (k, v) in inputs_ownership.iter() {
+                inputs_task_file_owners.insert(k.into(), v.clone().into());
+            }
+            request = request.inputs_ownership(inputs_task_file_owners);
+        }
+
+        if let Some(outputs_ownership) = outputs_ownership {
+            let mut outputs_task_file_owners: HashMap<String, OwnerList> = 
HashMap::new();
+            for (k, v) in outputs_ownership.iter() {
+                outputs_task_file_owners.insert(k.into(), v.clone().into());
+            }
+            request = request.outputs_ownership(outputs_task_file_owners);
+        }
+
+        let response = self.create_task_with_request(request)?;
+
+        Ok(response.task_id.to_string())
+    }
+
+    pub fn assign_data_with_request(
+        &mut self,
+        request: AssignDataRequest,
+    ) -> Result<AssignDataResponse> {
+        let response = self.api_client.assign_data(request)?;
+
+        Ok(response)
+    }
+
+    pub fn assign_data(
+        &mut self,
+        task_id: &str,
+        inputs: Option<HashMap<String, String>>,
+        outputs: Option<HashMap<String, String>>,
+    ) -> Result<()> {
+        let mut input_data = HashMap::new();
+        let mut output_data = HashMap::new();
+        if let Some(inputs) = inputs {
+            for (k, v) in inputs.iter() {
+                input_data.insert(k.into(), v.clone().try_into()?);
+            }
+        }
+
+        if let Some(outputs) = outputs {
+            for (k, v) in outputs.iter() {
+                output_data.insert(k.into(), v.clone().try_into()?);
+            }
+        }
+        let request = AssignDataRequest::new(task_id.try_into()?, input_data, 
output_data);
+        let _ = self.assign_data_with_request(request)?;
+
+        Ok(())
+    }
+
+    pub fn approve_task_with_request(
+        &mut self,
+        request: ApproveTaskRequest,
+    ) -> Result<ApproveTaskResponse> {
+        let response = self.api_client.approve_task(request)?;
+
+        Ok(response)
+    }
+
+    pub fn approve_task(&mut self, task_id: &str) -> Result<()> {
+        let request = ApproveTaskRequest::new(task_id.try_into()?);
+        let _ = self.approve_task_with_request(request)?;
+
+        Ok(())
+    }
+
+    pub fn invoke_task_with_request(
+        &mut self,
+        request: InvokeTaskRequest,
+    ) -> Result<InvokeTaskResponse> {
+        let response = self.api_client.invoke_task(request)?;
+
+        Ok(response)
+    }
+
+    pub fn invoke_task(&mut self, task_id: &str) -> Result<()> {
+        let request = InvokeTaskRequest::new(task_id.try_into()?);
+        let _ = self.invoke_task_with_request(request)?;
+
+        Ok(())
+    }
+
+    pub fn get_task_with_request(&mut self, request: GetTaskRequest) -> 
Result<GetTaskResponse> {
+        let response = self.api_client.get_task(request)?;
+
+        Ok(response)
+    }
+
+    pub fn get_task_result(&mut self, task_id: &str) -> Result<Vec<u8>> {
+        loop {
+            let request = GetTaskRequest::new(task_id.try_into()?);
+            let response = self.get_task_with_request(request)?;
+            if let TaskResult::Ok(task_outputs) = response.result {
+                return Ok(task_outputs.return_value);
+            }
+            let one_second = std::time::Duration::from_secs(1);
+            std::thread::sleep(one_second);
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use std::convert::TryInto;
+    use std::fs;
+    use teaclave_types::hashmap;
+
+    const ENCLAVE_INFO_PATH: &str = "../../release/services/enclave_info.toml";
+    #[cfg(dcap)]
+    const AS_ROOT_CA_CERT_PATH: &str = "../../keys/dcap_root_ca_cert.pem";
+    #[cfg(not(dcap))]
+    const AS_ROOT_CA_CERT_PATH: &str = "../../keys/ias_root_ca_cert.pem";
+    const USER_ID: &str = "rust_client_sdk_test_user";
+    const USER_PASSWORD: &str = "test_password";
+
+    #[test]
+    fn test_authentication_service() {
+        let enclave_info = EnclaveInfo::from_file(ENCLAVE_INFO_PATH).unwrap();
+        let bytes = fs::read(AS_ROOT_CA_CERT_PATH).unwrap();
+        let as_root_ca_cert = pem::parse(bytes).unwrap().contents;
+        let mut client =
+            AuthenticationService::connect("localhost:7776", &enclave_info, 
&as_root_ca_cert)
+                .unwrap();
+        let _ = client.user_register(USER_ID, USER_PASSWORD);
+        client.user_login(USER_ID, USER_PASSWORD).unwrap();
+    }
+
+    #[test]
+    fn test_frontend_service() {
+        let enclave_info = EnclaveInfo::from_file(ENCLAVE_INFO_PATH).unwrap();
+        let bytes = fs::read(AS_ROOT_CA_CERT_PATH).unwrap();
+        let as_root_ca_cert = pem::parse(bytes).unwrap().contents;
+        let mut client =
+            AuthenticationService::connect("localhost:7776", &enclave_info, 
&as_root_ca_cert)
+                .unwrap();
+        let _ = client.user_register(USER_ID, USER_PASSWORD);
+        let token = client.user_login(USER_ID, USER_PASSWORD).unwrap();
+
+        let mut client =
+            FrontendService::connect("localhost:7777", &enclave_info, 
&as_root_ca_cert).unwrap();
+        client.set_credential(USER_ID, &token);
+        let function_id = client
+            .register_function(
+                "builtin-echo",
+                "An native echo function.",
+                "builtin",
+                None,
+                Some(&["message"]),
+                None,
+                None,
+            )
+            .unwrap();
+        let _ = client.get_function(&function_id).unwrap();
+        let function_arguments = hashmap!("message" => "Hello, Teaclave!");
+        let task_id = client
+            .create_task(
+                &function_id,
+                Some(function_arguments),
+                "builtin",
+                None,
+                None,
+            )
+            .unwrap();
+
+        let _ = client.invoke_task(&task_id).unwrap();
+        let result = client.get_task_result(&task_id).unwrap();
+        assert_eq!(result, b"Hello, Teaclave!")
+    }
+
+    #[test]
+    fn test_frontend_service_with_request() {
+        let enclave_info = EnclaveInfo::from_file(ENCLAVE_INFO_PATH).unwrap();
+        let bytes = fs::read(AS_ROOT_CA_CERT_PATH).unwrap();
+        let as_root_ca_cert = pem::parse(bytes).unwrap().contents;
+        let mut client =
+            AuthenticationService::connect("localhost:7776", &enclave_info, 
&as_root_ca_cert)
+                .unwrap();
+        let _ = client.user_register(USER_ID, USER_PASSWORD);
+        let token = client.user_login(USER_ID, USER_PASSWORD).unwrap();
+
+        let mut client =
+            FrontendService::connect("localhost:7777", &enclave_info, 
&as_root_ca_cert).unwrap();
+        client.set_credential(USER_ID, &token);
+        let request = RegisterFunctionRequest::default();
+        let function_id = client
+            .register_function_with_request(request)
+            .unwrap()
+            .function_id;
+
+        let request = GetFunctionRequest::new(function_id);
+        let response = client.get_function_with_request(request);
+        assert!(response.is_ok());
+
+        let function_id = "function-00000000-0000-0000-0000-000000000002"
+            .to_string()
+            .try_into()
+            .unwrap();
+        let request = CreateTaskRequest::new()
+            .function_id(function_id)
+            .function_arguments(hashmap!("arg1" => "arg1_value"))
+            .executor(Executor::MesaPy)
+            .outputs_ownership(hashmap!("output" =>  vec!["frontend_user", 
"mock_user"]));
+        let response = client.create_task_with_request(request);
+        assert!(response.is_ok());
+        let task_id = response.unwrap().task_id;
+
+        let request = GetTaskRequest::new(task_id);
+        let response = client.get_task_with_request(request);
+        assert!(response.is_ok());
+    }
+
+    #[test]
+    fn test_assign_data() {
+        let enclave_info = EnclaveInfo::from_file(ENCLAVE_INFO_PATH).unwrap();
+        let bytes = fs::read(AS_ROOT_CA_CERT_PATH).unwrap();
+        let as_root_ca_cert = pem::parse(bytes).unwrap().contents;
+        let mut client =
+            AuthenticationService::connect("localhost:7776", &enclave_info, 
&as_root_ca_cert)
+                .unwrap();
+        let _ = client.user_register(USER_ID, USER_PASSWORD);
+        let token = client.user_login(USER_ID, USER_PASSWORD).unwrap();
+
+        let mut client =
+            FrontendService::connect("localhost:7777", &enclave_info, 
&as_root_ca_cert).unwrap();
+        client.set_credential(USER_ID, &token);
+        let function_id = "function-00000000-0000-0000-0000-000000000002";
+        let function_arguments = hashmap!("arg1" => "arg1_value");
+        let outputs_ownership = hashmap!("output" => 
vec![USER_ID.to_string()]);
+        let task_id = client
+            .create_task(
+                &function_id,
+                Some(function_arguments),
+                "mesapy",
+                None,
+                Some(outputs_ownership),
+            )
+            .unwrap();
+        let data_id = client
+            .register_output_file(
+                "https://external-storage.com/filepath?presigned_token";,
+                FileCrypto::default(),
+            )
+            .unwrap();
+        let outputs = hashmap!("output" => data_id);
+        client.assign_data(&task_id, None, Some(outputs)).unwrap();
+    }
+
+    #[test]
+    fn test_assign_data_err() {
+        let enclave_info = EnclaveInfo::from_file(ENCLAVE_INFO_PATH).unwrap();
+        let bytes = fs::read(AS_ROOT_CA_CERT_PATH).unwrap();
+        let as_root_ca_cert = pem::parse(bytes).unwrap().contents;
+        let mut client =
+            AuthenticationService::connect("localhost:7776", &enclave_info, 
&as_root_ca_cert)
+                .unwrap();
+        let _ = client.user_register(USER_ID, USER_PASSWORD);
+        let token = client.user_login(USER_ID, USER_PASSWORD).unwrap();
+
+        let mut client =
+            FrontendService::connect("localhost:7777", &enclave_info, 
&as_root_ca_cert).unwrap();
+        client.set_credential(USER_ID, &token);
+        let function_id = "function-00000000-0000-0000-0000-000000000002";
+        let function_arguments = hashmap!("arg1" => "arg1_value");
+        let outputs_ownership = hashmap!("output" => 
vec!["incorrect_user".to_string()]);
+        let task_id = client
+            .create_task(
+                &function_id,
+                Some(function_arguments),
+                "mesapy",
+                None,
+                Some(outputs_ownership),
+            )
+            .unwrap();
+        let data_id = client
+            .register_output_file(
+                "https://external-storage.com/filepath?presigned_token";,
+                FileCrypto::default(),
+            )
+            .unwrap();
+        let outputs = hashmap!("output" => data_id);
+        let result = client.assign_data(&task_id, None, Some(outputs));
+        assert!(result.is_err());
+    }
+
+    #[test]
+    fn test_approve_task() {
+        let enclave_info = EnclaveInfo::from_file(ENCLAVE_INFO_PATH).unwrap();
+        let bytes = fs::read(AS_ROOT_CA_CERT_PATH).unwrap();
+        let as_root_ca_cert = pem::parse(bytes).unwrap().contents;
+        let mut client =
+            AuthenticationService::connect("localhost:7776", &enclave_info, 
&as_root_ca_cert)
+                .unwrap();
+        let _ = client.user_register(USER_ID, USER_PASSWORD);
+        let token = client.user_login(USER_ID, USER_PASSWORD).unwrap();
+
+        let mut client =
+            FrontendService::connect("localhost:7777", &enclave_info, 
&as_root_ca_cert).unwrap();
+        client.set_credential(USER_ID, &token);
+        let function_id = "function-00000000-0000-0000-0000-000000000002";
+        let function_arguments = hashmap!("arg1" => "arg1_value");
+        let outputs_ownership = hashmap!("output" => 
vec![USER_ID.to_string()]);
+        let task_id = client
+            .create_task(
+                &function_id,
+                Some(function_arguments),
+                "mesapy",
+                None,
+                Some(outputs_ownership),
+            )
+            .unwrap();
+        let data_id = client
+            .register_output_file(
+                "https://external-storage.com/filepath?presigned_token";,
+                FileCrypto::default(),
+            )
+            .unwrap();
+        let outputs = hashmap!("output" => data_id);
+        client.assign_data(&task_id, None, Some(outputs)).unwrap();
+        client.approve_task(&task_id).unwrap();
+    }
+}
diff --git a/services/proto/build.rs b/services/proto/build.rs
index b734a01..31ac30f 100644
--- a/services/proto/build.rs
+++ b/services/proto/build.rs
@@ -17,6 +17,7 @@
 
 use std::env;
 use std::path::Path;
+use std::path::PathBuf;
 use std::process::Command;
 use std::str;
 
@@ -40,11 +41,20 @@ fn main() {
         println!("cargo:rerun-if-changed={}", pf);
     }
 
-    let target_dir = 
Path::new(&env::var("TEACLAVE_SYMLINKS").expect("TEACLAVE_SYMLINKS"))
-        .join("teaclave_build/target/proto_gen");
-    let unix_toml_dir = 
env::var("MT_SGXAPP_TOML_DIR").expect("MT_SGXAPP_TOML_DIR");
+    let target_dir = match env::var("TEACLAVE_SYMLINKS") {
+        Ok(teaclave_symlinks) => {
+            
Path::new(&teaclave_symlinks).join("teaclave_build/target/proto_gen")
+        }
+        Err(_) => env::current_dir().unwrap().join("target/proto_gen"),
+    };
+    let current_dir: PathBuf = match env::var("MT_SGXAPP_TOML_DIR") {
+        Ok(sgxapp_toml_dir) => Path::new(&sgxapp_toml_dir).into(),
+        // This fallback is only for compiling rust client sdk with cargo
+        Err(_) => Path::new("../../").into(),
+    };
+
     let c = Command::new("cargo")
-        .current_dir(&unix_toml_dir)
+        .current_dir(&current_dir)
         .args(&[
             "run",
             "--target-dir",
diff --git a/services/proto/proto_gen/templates/proto.j2 
b/services/proto/proto_gen/templates/proto.j2
index 361573f..0faf481 100644
--- a/services/proto/proto_gen/templates/proto.j2
+++ b/services/proto/proto_gen/templates/proto.j2
@@ -103,4 +103,8 @@ impl {{ service.proto_name }}Client {
     pub fn metadata_mut(&mut self) -> &mut 
std::collections::HashMap<std::string::String, std::string::String> {
         &mut self.metadata
     }
+
+    pub fn set_metadata(&mut self, metadata: 
std::collections::HashMap<std::string::String, std::string::String>) {
+        self.metadata = metadata
+    }
 }
diff --git a/services/proto/src/teaclave_frontend_service.rs 
b/services/proto/src/teaclave_frontend_service.rs
index e784f59..311e397 100644
--- a/services/proto/src/teaclave_frontend_service.rs
+++ b/services/proto/src/teaclave_frontend_service.rs
@@ -420,11 +420,8 @@ impl CreateTaskRequest {
         }
     }
 
-    pub fn executor(self, executor: impl Into<Executor>) -> Self {
-        Self {
-            executor: executor.into(),
-            ..self
-        }
+    pub fn executor(self, executor: Executor) -> Self {
+        Self { executor, ..self }
     }
 
     pub fn inputs_ownership(self, map: impl Into<TaskFileOwners>) -> Self {
diff --git a/types/Cargo.toml b/types/Cargo.toml
index f13043b..a2e3fc7 100644
--- a/types/Cargo.toml
+++ b/types/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
 default = [
     "protected_fs_rs/default",
 ]
+app = [ "default" ]
 mesalock_sgx = [
     "sgx_tstd",
     "teaclave_crypto/mesalock_sgx",
diff --git a/types/src/attestation.rs b/types/src/attestation.rs
index 0b1e2c0..24f5139 100644
--- a/types/src/attestation.rs
+++ b/types/src/attestation.rs
@@ -91,6 +91,12 @@ impl EnclaveInfo {
         Ok(Self::from_bytes(enclave_info))
     }
 
+    #[cfg(feature = "app")]
+    pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
+        let bytes = std::fs::read(path)?;
+        Ok(Self::from_bytes(&bytes))
+    }
+
     pub fn from_bytes(enclave_info: &[u8]) -> Self {
         let config: EnclaveInfoToml = toml::from_slice(enclave_info)
             .expect("Content not correct, unable to load enclave info.");
diff --git a/types/src/crypto.rs b/types/src/crypto.rs
index 85612a8..8c03586 100644
--- a/types/src/crypto.rs
+++ b/types/src/crypto.rs
@@ -33,6 +33,13 @@ pub struct FileAuthTag {
 }
 
 impl FileAuthTag {
+    pub fn from_bytes(input: &[u8]) -> Result<Self> {
+        ensure!(input.len() == FILE_AUTH_TAG_LENGTH, "Invalid length");
+        let mut file_auth_tag = FileAuthTag::default();
+        file_auth_tag.tag.clone_from_slice(&input);
+        Ok(file_auth_tag)
+    }
+
     pub fn from_hex(input: impl AsRef<str>) -> Result<Self> {
         let hex = hex::decode(input.as_ref()).context("Illegal AuthTag 
provided")?;
         let tag = hex


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to